diff options
Diffstat (limited to 'OpenSim/Services/FreeswitchService/FreeswitchService.cs')
-rw-r--r-- | OpenSim/Services/FreeswitchService/FreeswitchService.cs | 360 |
1 files changed, 358 insertions, 2 deletions
diff --git a/OpenSim/Services/FreeswitchService/FreeswitchService.cs b/OpenSim/Services/FreeswitchService/FreeswitchService.cs index 0a38300..201e72f 100644 --- a/OpenSim/Services/FreeswitchService/FreeswitchService.cs +++ b/OpenSim/Services/FreeswitchService/FreeswitchService.cs | |||
@@ -26,6 +26,7 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Text; | ||
29 | using System.Reflection; | 30 | using System.Reflection; |
30 | using Nini.Config; | 31 | using Nini.Config; |
31 | using log4net; | 32 | using log4net; |
@@ -33,19 +34,374 @@ using OpenSim.Framework; | |||
33 | using OpenSim.Data; | 34 | using OpenSim.Data; |
34 | using OpenSim.Services.Interfaces; | 35 | using OpenSim.Services.Interfaces; |
35 | using OpenMetaverse; | 36 | using OpenMetaverse; |
37 | using OpenMetaverse.StructuredData; | ||
38 | using System.Collections; | ||
36 | 39 | ||
37 | namespace OpenSim.Services.FreeswitchService | 40 | namespace OpenSim.Services.FreeswitchService |
38 | { | 41 | { |
39 | public class FreeswitchService : FreeswitchServiceBase, IFreeswitchService | 42 | public class FreeswitchService : FreeswitchServiceBase, IFreeswitchService |
40 | { | 43 | { |
41 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
42 | 45 | ||
43 | public FreeswitchService(IConfigSource config) : base(config) | 46 | public FreeswitchService(IConfigSource config) : base(config) |
44 | { | 47 | { |
45 | // Perform initilialization here | 48 | // Perform initilialization here |
46 | } | 49 | } |
47 | 50 | ||
51 | public Hashtable HandleDialplanRequest(Hashtable request) | ||
52 | { | ||
53 | m_log.DebugFormat("[FreeSwitchVoice]: HandleDialplanRequest called with {0}",request.ToString()); | ||
54 | |||
55 | Hashtable response = new Hashtable(); | ||
56 | |||
57 | // foreach (DictionaryEntry item in request) | ||
58 | // { | ||
59 | //// m_log.InfoFormat("[FreeSwitchDirectory]: requestBody item {0} {1}",item.Key, item.Value); | ||
60 | // } | ||
61 | |||
62 | string requestcontext = (string) request["Hunt-Context"]; | ||
63 | response["content_type"] = "text/xml"; | ||
64 | response["keepalive"] = false; | ||
65 | response["int_response_code"] = 200; | ||
66 | |||
67 | if (m_freeSwitchContext != String.Empty && m_freeSwitchContext != requestcontext) | ||
68 | { | ||
69 | m_log.Debug("[FreeSwitchDirectory]: returning empty as it's for another context"); | ||
70 | response["str_response_string"] = ""; | ||
71 | } | ||
72 | else | ||
73 | { | ||
74 | response["str_response_string"] = String.Format(@"<?xml version=""1.0"" encoding=""utf-8""?> | ||
75 | <document type=""freeswitch/xml""> | ||
76 | <section name=""dialplan""> | ||
77 | <context name=""{0}"">" + | ||
78 | |||
79 | /* <!-- dial via SIP uri --> | ||
80 | <extension name=""sip_uri""> | ||
81 | <condition field=""destination_number"" expression=""^sip:(.*)$""> | ||
82 | <action application=""bridge"" data=""sofia/${use_profile}/$1""/> | ||
83 | <!--<action application=""bridge"" data=""$1""/>--> | ||
84 | </condition> | ||
85 | </extension>*/ | ||
86 | |||
87 | @"<extension name=""opensim_conferences""> | ||
88 | <condition field=""destination_number"" expression=""^confctl-(.*)$""> | ||
89 | <action application=""answer""/> | ||
90 | <action application=""conference"" data=""$1-{1}@{0}""/> | ||
91 | </condition> | ||
92 | </extension> | ||
93 | |||
94 | <extension name=""opensim_conf""> | ||
95 | <condition field=""destination_number"" expression=""^conf-(.*)$""> | ||
96 | <action application=""answer""/> | ||
97 | <action application=""conference"" data=""$1-{1}@{0}""/> | ||
98 | </condition> | ||
99 | </extension> | ||
100 | |||
101 | <extension name=""avatar""> | ||
102 | <condition field=""destination_number"" expression=""^(x.*)$""> | ||
103 | <action application=""bridge"" data=""user/$1""/> | ||
104 | </condition> | ||
105 | </extension> | ||
106 | |||
107 | </context> | ||
108 | </section> | ||
109 | </document>", m_freeSwitchContext, m_freeSwitchRealm); | ||
110 | } | ||
111 | |||
112 | return response; | ||
113 | } | ||
114 | |||
115 | public Hashtable HandleDirectoryRequest(Hashtable request) | ||
116 | { | ||
117 | Hashtable response = new Hashtable(); | ||
118 | string domain = (string) request["domain"]; | ||
119 | if (domain != m_freeSwitchRealm) | ||
120 | { | ||
121 | response["content_type"] = "text/xml"; | ||
122 | response["keepalive"] = false; | ||
123 | response["int_response_code"] = 200; | ||
124 | response["str_response_string"] = ""; | ||
125 | } | ||
126 | else | ||
127 | { | ||
128 | // m_log.DebugFormat("[FreeSwitchDirectory]: HandleDirectoryRequest called with {0}",request.ToString()); | ||
129 | |||
130 | // information in the request we might be interested in | ||
131 | |||
132 | // Request 1 sip_auth for users account | ||
133 | |||
134 | //Event-Calling-Function=sofia_reg_parse_auth | ||
135 | //Event-Calling-Line-Number=1494 | ||
136 | //action=sip_auth | ||
137 | //sip_user_agent=Vivox-SDK-2.1.3010.6151-Mac%20(Feb-11-2009/16%3A42%3A41) | ||
138 | //sip_auth_username=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) | ||
139 | //sip_auth_realm=9.20.151.43 | ||
140 | //sip_contact_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) | ||
141 | //sip_contact_host=192.168.0.3 // this shouldnt really be a local IP, investigate STUN servers | ||
142 | //sip_to_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D | ||
143 | //sip_to_host=9.20.151.43 | ||
144 | //sip_auth_method=REGISTER | ||
145 | //user=xhZuXKmRpECyr2AARJYyGgg%3D%3D | ||
146 | //domain=9.20.151.43 | ||
147 | //ip=9.167.220.137 // this is the correct IP rather than sip_contact_host above when through a vpn or NAT setup | ||
148 | |||
149 | // foreach (DictionaryEntry item in request) | ||
150 | // m_log.DebugFormat("[FreeSwitchDirectory]: requestBody item {0} {1}", item.Key, item.Value); | ||
151 | |||
152 | string eventCallingFunction = (string) request["Event-Calling-Function"]; | ||
153 | if (eventCallingFunction == null) | ||
154 | { | ||
155 | eventCallingFunction = "sofia_reg_parse_auth"; | ||
156 | } | ||
157 | |||
158 | if (eventCallingFunction.Length == 0) | ||
159 | { | ||
160 | eventCallingFunction = "sofia_reg_parse_auth"; | ||
161 | } | ||
162 | |||
163 | if (eventCallingFunction == "sofia_reg_parse_auth") | ||
164 | { | ||
165 | string sipAuthMethod = (string)request["sip_auth_method"]; | ||
166 | |||
167 | if (sipAuthMethod == "REGISTER") | ||
168 | { | ||
169 | response = HandleRegister(m_freeSwitchContext, m_freeSwitchRealm, request); | ||
170 | } | ||
171 | else if (sipAuthMethod == "INVITE") | ||
172 | { | ||
173 | response = HandleInvite(m_freeSwitchContext, m_freeSwitchRealm, request); | ||
174 | } | ||
175 | else | ||
176 | { | ||
177 | m_log.ErrorFormat("[FreeSwitchVoice]: HandleDirectoryRequest unknown sip_auth_method {0}",sipAuthMethod); | ||
178 | response["int_response_code"] = 404; | ||
179 | response["content_type"] = "text/xml"; | ||
180 | response["str_response_string"] = ""; | ||
181 | } | ||
182 | } | ||
183 | else if (eventCallingFunction == "switch_xml_locate_user") | ||
184 | { | ||
185 | response = HandleLocateUser(m_freeSwitchRealm, request); | ||
186 | } | ||
187 | else if (eventCallingFunction == "user_data_function") // gets called when an avatar to avatar call is made | ||
188 | { | ||
189 | response = HandleLocateUser(m_freeSwitchRealm, request); | ||
190 | } | ||
191 | else if (eventCallingFunction == "user_outgoing_channel") | ||
192 | { | ||
193 | response = HandleRegister(m_freeSwitchContext, m_freeSwitchRealm, request); | ||
194 | } | ||
195 | else if (eventCallingFunction == "config_sofia") // happens once on freeswitch startup | ||
196 | { | ||
197 | response = HandleConfigSofia(m_freeSwitchContext, m_freeSwitchRealm, request); | ||
198 | } | ||
199 | else if (eventCallingFunction == "switch_load_network_lists") | ||
200 | { | ||
201 | //response = HandleLoadNetworkLists(request); | ||
202 | response["int_response_code"] = 404; | ||
203 | response["keepalive"] = false; | ||
204 | response["content_type"] = "text/xml"; | ||
205 | response["str_response_string"] = ""; | ||
206 | } | ||
207 | else | ||
208 | { | ||
209 | m_log.ErrorFormat("[FreeSwitchVoice]: HandleDirectoryRequest unknown Event-Calling-Function {0}",eventCallingFunction); | ||
210 | response["int_response_code"] = 404; | ||
211 | response["keepalive"] = false; | ||
212 | response["content_type"] = "text/xml"; | ||
213 | response["str_response_string"] = ""; | ||
214 | } | ||
215 | } | ||
216 | return response; | ||
217 | } | ||
218 | |||
219 | private Hashtable HandleRegister(string Context, string Realm, Hashtable request) | ||
220 | { | ||
221 | m_log.Info("[FreeSwitchDirectory]: HandleRegister called"); | ||
222 | |||
223 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
224 | string password = "1234"; | ||
225 | string domain = (string) request["domain"]; | ||
226 | string user = (string) request["user"]; | ||
227 | |||
228 | Hashtable response = new Hashtable(); | ||
229 | response["content_type"] = "text/xml"; | ||
230 | response["keepalive"] = false; | ||
231 | response["int_response_code"] = 200; | ||
232 | |||
233 | response["str_response_string"] = String.Format( | ||
234 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
235 | "<document type=\"freeswitch/xml\">\r\n" + | ||
236 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
237 | "<domain name=\"{0}\">\r\n" + | ||
238 | "<user id=\"{1}\">\r\n" + | ||
239 | "<params>\r\n" + | ||
240 | "<param name=\"password\" value=\"{2}\" />\r\n" + | ||
241 | "<param name=\"dial-string\" value=\"{{sip_contact_user={1}}}{{presence_id=${{dialed_user}}@${{dialed_domain}}}}${{sofia_contact(${{dialed_user}}@${{dialed_domain}})}}\"/>\r\n" + | ||
242 | "</params>\r\n" + | ||
243 | "<variables>\r\n" + | ||
244 | "<variable name=\"user_context\" value=\"{3}\" />\r\n" + | ||
245 | "<variable name=\"presence_id\" value=\"{1}@{0}\"/>"+ | ||
246 | "</variables>\r\n" + | ||
247 | "</user>\r\n" + | ||
248 | "</domain>\r\n" + | ||
249 | "</section>\r\n" + | ||
250 | "</document>\r\n", | ||
251 | domain , user, password, Context); | ||
252 | |||
253 | return response; | ||
254 | } | ||
255 | |||
256 | private Hashtable HandleInvite(string Context, string Realm, Hashtable request) | ||
257 | { | ||
258 | m_log.Info("[FreeSwitchDirectory]: HandleInvite called"); | ||
259 | |||
260 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
261 | string password = "1234"; | ||
262 | string domain = (string) request["domain"]; | ||
263 | string user = (string) request["user"]; | ||
264 | string sipRequestUser = (string) request["sip_request_user"]; | ||
265 | |||
266 | Hashtable response = new Hashtable(); | ||
267 | response["content_type"] = "text/xml"; | ||
268 | response["keepalive"] = false; | ||
269 | response["int_response_code"] = 200; | ||
270 | response["str_response_string"] = String.Format( | ||
271 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
272 | "<document type=\"freeswitch/xml\">\r\n" + | ||
273 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
274 | "<domain name=\"{0}\">\r\n" + | ||
275 | "<user id=\"{1}\">\r\n" + | ||
276 | "<params>\r\n" + | ||
277 | "<param name=\"password\" value=\"{2}\" />\r\n" + | ||
278 | "<param name=\"dial-string\" value=\"{{sip_contact_user={1}}}{{presence_id=${1}@${{dialed_domain}}}}${{sofia_contact(${1}@${{dialed_domain}})}}\"/>\r\n" + | ||
279 | "</params>\r\n" + | ||
280 | "<variables>\r\n" + | ||
281 | "<variable name=\"user_context\" value=\"{4}\" />\r\n" + | ||
282 | "<variable name=\"presence_id\" value=\"{1}@$${{domain}}\"/>"+ | ||
283 | "</variables>\r\n" + | ||
284 | "</user>\r\n" + | ||
285 | "<user id=\"{3}\">\r\n" + | ||
286 | "<params>\r\n" + | ||
287 | "<param name=\"password\" value=\"{2}\" />\r\n" + | ||
288 | "<param name=\"dial-string\" value=\"{{sip_contact_user={1}}}{{presence_id=${3}@${{dialed_domain}}}}${{sofia_contact(${3}@${{dialed_domain}})}}\"/>\r\n" + | ||
289 | "</params>\r\n" + | ||
290 | "<variables>\r\n" + | ||
291 | "<variable name=\"user_context\" value=\"{4}\" />\r\n" + | ||
292 | "<variable name=\"presence_id\" value=\"{3}@$${{domain}}\"/>"+ | ||
293 | "</variables>\r\n" + | ||
294 | "</user>\r\n" + | ||
295 | "</domain>\r\n" + | ||
296 | "</section>\r\n" + | ||
297 | "</document>\r\n", | ||
298 | domain , user, password,sipRequestUser, Context); | ||
299 | |||
300 | return response; | ||
301 | } | ||
48 | 302 | ||
49 | // Implement IFreeswitchService here | 303 | private Hashtable HandleLocateUser(String Realm, Hashtable request) |
304 | { | ||
305 | m_log.Info("[FreeSwitchDirectory]: HandleLocateUser called"); | ||
306 | |||
307 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
308 | string domain = (string) request["domain"]; | ||
309 | string user = (string) request["user"]; | ||
310 | |||
311 | Hashtable response = new Hashtable(); | ||
312 | response["content_type"] = "text/xml"; | ||
313 | response["keepalive"] = false; | ||
314 | response["int_response_code"] = 200; | ||
315 | response["str_response_string"] = String.Format( | ||
316 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
317 | "<document type=\"freeswitch/xml\">\r\n" + | ||
318 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
319 | "<domain name=\"{0}\">\r\n" + | ||
320 | "<params>\r\n" + | ||
321 | "<param name=\"dial-string\" value=\"{{sip_contact_user=${{dialed_user}}}}{{presence_id=${{dialed_user}}@${{dialed_domain}}}}${{sofia_contact(${{dialed_user}}@${{dialed_domain}})}}\"/>\r\n" + | ||
322 | "</params>\r\n" + | ||
323 | "<user id=\"{1}\">\r\n" + | ||
324 | "<variables>\r\n"+ | ||
325 | "<variable name=\"default_gateway\" value=\"$${{default_provider}}\"/>\r\n"+ | ||
326 | "<variable name=\"presence_id\" value=\"{1}@$${{domain}}\"/>"+ | ||
327 | "</variables>\r\n"+ | ||
328 | "</user>\r\n" + | ||
329 | "</domain>\r\n" + | ||
330 | "</section>\r\n" + | ||
331 | "</document>\r\n", | ||
332 | domain , user); | ||
333 | |||
334 | return response; | ||
335 | } | ||
336 | |||
337 | private Hashtable HandleConfigSofia(string Context, string Realm, Hashtable request) | ||
338 | { | ||
339 | m_log.Info("[FreeSwitchDirectory]: HandleConfigSofia called."); | ||
340 | |||
341 | // TODO the password we return needs to match that sent in the request, this is hard coded for now | ||
342 | string domain = (string) request["domain"]; | ||
343 | |||
344 | Hashtable response = new Hashtable(); | ||
345 | response["content_type"] = "text/xml"; | ||
346 | response["keepalive"] = false; | ||
347 | response["int_response_code"] = 200; | ||
348 | response["str_response_string"] = String.Format( | ||
349 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + | ||
350 | "<document type=\"freeswitch/xml\">\r\n" + | ||
351 | "<section name=\"directory\" description=\"User Directory\">\r\n" + | ||
352 | "<domain name=\"{0}\">\r\n" + | ||
353 | "<params>\r\n" + | ||
354 | "<param name=\"dial-string\" value=\"{{sip_contact_user=${{dialed_user}}}}{{presence_id=${{dialed_user}}@${{dialed_domain}}}}${{sofia_contact(${{dialed_user}}@${{dialed_domain}})}}\"/>\r\n" + | ||
355 | "</params>\r\n" + | ||
356 | "<groups name=\"default\">\r\n"+ | ||
357 | "<users>\r\n"+ | ||
358 | "<user id=\"$${{default_provider}}\">\r\n"+ | ||
359 | "<gateways>\r\n"+ | ||
360 | "<gateway name=\"$${{default_provider}}\">\r\n"+ | ||
361 | "<param name=\"username\" value=\"$${{default_provider_username}}\"/>\r\n"+ | ||
362 | "<param name=\"password\" value=\"$${{default_provider_password}}\"/>\r\n"+ | ||
363 | "<param name=\"from-user\" value=\"$${{default_provider_username}}\"/>\r\n"+ | ||
364 | "<param name=\"from-domain\" value=\"$${{default_provider_from_domain}}\"/>\r\n"+ | ||
365 | "<param name=\"expire-seconds\" value=\"600\"/>\r\n"+ | ||
366 | "<param name=\"register\" value=\"$${{default_provider_register}}\"/>\r\n"+ | ||
367 | "<param name=\"retry-seconds\" value=\"30\"/>\r\n"+ | ||
368 | "<param name=\"extension\" value=\"$${{default_provider_contact}}\"/>\r\n"+ | ||
369 | "<param name=\"contact-params\" value=\"domain_name=$${{domain}}\"/>\r\n"+ | ||
370 | "<param name=\"context\" value=\"{1}\"/>\r\n"+ | ||
371 | "</gateway>\r\n"+ | ||
372 | "</gateways>\r\n"+ | ||
373 | "<params>\r\n"+ | ||
374 | "<param name=\"password\" value=\"$${{default_provider_password}}\"/>\r\n"+ | ||
375 | "</params>\r\n"+ | ||
376 | "</user>\r\n"+ | ||
377 | "</users>"+ | ||
378 | "</groups>\r\n" + | ||
379 | "<variables>\r\n"+ | ||
380 | "<variable name=\"default_gateway\" value=\"$${{default_provider}}\"/>\r\n"+ | ||
381 | "</variables>\r\n"+ | ||
382 | "</domain>\r\n" + | ||
383 | "</section>\r\n" + | ||
384 | "</document>\r\n", | ||
385 | domain, Context); | ||
386 | |||
387 | return response; | ||
388 | } | ||
389 | |||
390 | public string GetJsonConfig() | ||
391 | { | ||
392 | OSDMap map = new OSDMap(9); | ||
393 | |||
394 | map.Add("Realm", m_freeSwitchRealm); | ||
395 | map.Add("SIPProxy", m_freeSwitchSIPProxy); | ||
396 | map.Add("AttemptUseSTUN", m_freeSwitchAttemptUseSTUN); | ||
397 | map.Add("EchoServer", m_freeSwitchEchoServer); | ||
398 | map.Add("EchoPort", m_freeSwitchEchoPort); | ||
399 | map.Add("DefaultWellKnownIP", m_freeSwitchDefaultWellKnownIP); | ||
400 | map.Add("DefaultTimeout", m_freeSwitchDefaultTimeout); | ||
401 | map.Add("Context", m_freeSwitchContext); | ||
402 | map.Add("APIPrefix", m_freeSwitchAPIPrefix); | ||
403 | |||
404 | return OSDParser.SerializeJsonString(map); | ||
405 | } | ||
50 | } | 406 | } |
51 | } | 407 | } |