opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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 OpenSimulator 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.Net.Security;
32 using System.Web;
33 using System.Security.Cryptography.X509Certificates;
34 using System.Text;
35 using System.Xml;
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Reflection;
39 using OpenMetaverse;
40 using OpenMetaverse.StructuredData;
41 using log4net;
42 using Nini.Config;
43 using Nwc.XmlRpc;
44 using OpenSim.Framework;
45 using Mono.Addins;
46  
47 using OpenSim.Framework.Capabilities;
48 using OpenSim.Framework.Servers;
49 using OpenSim.Framework.Servers.HttpServer;
50 using OpenSim.Region.Framework.Interfaces;
51 using OpenSim.Region.Framework.Scenes;
52 using Caps = OpenSim.Framework.Capabilities.Caps;
53 using System.Text.RegularExpressions;
54 using OpenSim.Server.Base;
55 using OpenSim.Services.Interfaces;
56 using OSDMap = OpenMetaverse.StructuredData.OSDMap;
57  
58 namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice
59 {
60 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FreeSwitchVoiceModule")]
61 public class FreeSwitchVoiceModule : ISharedRegionModule, IVoiceModule
62 {
63 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
64  
65 // Capability string prefixes
66 private static readonly string m_parcelVoiceInfoRequestPath = "0207/";
67 private static readonly string m_provisionVoiceAccountRequestPath = "0208/";
68 private static readonly string m_chatSessionRequestPath = "0209/";
69  
70 // Control info
71 private static bool m_Enabled = false;
72  
73 // FreeSwitch server is going to contact us and ask us all
74 // sorts of things.
75  
76 // SLVoice client will do a GET on this prefix
77 private static string m_freeSwitchAPIPrefix;
78  
79 // We need to return some information to SLVoice
80 // figured those out via curl
81 // http://vd1.vivox.com/api2/viv_get_prelogin.php
82 //
83 // need to figure out whether we do need to return ALL of
84 // these...
85 private static string m_freeSwitchRealm;
86 private static string m_freeSwitchSIPProxy;
87 private static bool m_freeSwitchAttemptUseSTUN;
88 private static string m_freeSwitchEchoServer;
89 private static int m_freeSwitchEchoPort;
90 private static string m_freeSwitchDefaultWellKnownIP;
91 private static int m_freeSwitchDefaultTimeout;
92 private static string m_freeSwitchUrlResetPassword;
93 private uint m_freeSwitchServicePort;
94 private string m_openSimWellKnownHTTPAddress;
95 // private string m_freeSwitchContext;
96  
97 private readonly Dictionary<string, string> m_UUIDName = new Dictionary<string, string>();
98 private Dictionary<string, string> m_ParcelAddress = new Dictionary<string, string>();
99  
100 private IConfig m_Config;
101  
102 private IFreeswitchService m_FreeswitchService;
103  
104 public void Initialise(IConfigSource config)
105 {
106 m_Config = config.Configs["FreeSwitchVoice"];
107  
108 if (m_Config == null)
109 return;
110  
111 if (!m_Config.GetBoolean("Enabled", false))
112 return;
113  
114 try
115 {
116 string serviceDll = m_Config.GetString("LocalServiceModule",
117 String.Empty);
118  
119 if (serviceDll == String.Empty)
120 {
121 m_log.Error("[FreeSwitchVoice]: No LocalServiceModule named in section FreeSwitchVoice. Not starting.");
122 return;
123 }
124  
125 Object[] args = new Object[] { config };
126 m_FreeswitchService = ServerUtils.LoadPlugin<IFreeswitchService>(serviceDll, args);
127  
128 string jsonConfig = m_FreeswitchService.GetJsonConfig();
129 //m_log.Debug("[FreeSwitchVoice]: Configuration string: " + jsonConfig);
130 OSDMap map = (OSDMap)OSDParser.DeserializeJson(jsonConfig);
131  
132 m_freeSwitchAPIPrefix = map["APIPrefix"].AsString();
133 m_freeSwitchRealm = map["Realm"].AsString();
134 m_freeSwitchSIPProxy = map["SIPProxy"].AsString();
135 m_freeSwitchAttemptUseSTUN = map["AttemptUseSTUN"].AsBoolean();
136 m_freeSwitchEchoServer = map["EchoServer"].AsString();
137 m_freeSwitchEchoPort = map["EchoPort"].AsInteger();
138 m_freeSwitchDefaultWellKnownIP = map["DefaultWellKnownIP"].AsString();
139 m_freeSwitchDefaultTimeout = map["DefaultTimeout"].AsInteger();
140 m_freeSwitchUrlResetPassword = String.Empty;
141 // m_freeSwitchContext = map["Context"].AsString();
142  
143 if (String.IsNullOrEmpty(m_freeSwitchRealm) ||
144 String.IsNullOrEmpty(m_freeSwitchAPIPrefix))
145 {
146 m_log.Error("[FreeSwitchVoice]: Freeswitch service mis-configured. Not starting.");
147 return;
148 }
149  
150 // set up http request handlers for
151 // - prelogin: viv_get_prelogin.php
152 // - signin: viv_signin.php
153 // - buddies: viv_buddy.php
154 // - ???: viv_watcher.php
155 // - signout: viv_signout.php
156 MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_get_prelogin.php", m_freeSwitchAPIPrefix),
157 FreeSwitchSLVoiceGetPreloginHTTPHandler);
158  
159 MainServer.Instance.AddHTTPHandler(String.Format("{0}/freeswitch-config", m_freeSwitchAPIPrefix), FreeSwitchConfigHTTPHandler);
160  
161 // RestStreamHandler h = new
162 // RestStreamHandler("GET",
163 // String.Format("{0}/viv_get_prelogin.php", m_freeSwitchAPIPrefix), FreeSwitchSLVoiceGetPreloginHTTPHandler);
164 // MainServer.Instance.AddStreamHandler(h);
165  
166 MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_signin.php", m_freeSwitchAPIPrefix),
167 FreeSwitchSLVoiceSigninHTTPHandler);
168  
169 MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_buddy.php", m_freeSwitchAPIPrefix),
170 FreeSwitchSLVoiceBuddyHTTPHandler);
171  
172 MainServer.Instance.AddHTTPHandler(String.Format("{0}/viv_watcher.php", m_freeSwitchAPIPrefix),
173 FreeSwitchSLVoiceWatcherHTTPHandler);
174  
175 m_log.InfoFormat("[FreeSwitchVoice]: using FreeSwitch server {0}", m_freeSwitchRealm);
176  
177 m_Enabled = true;
178  
179 m_log.Info("[FreeSwitchVoice]: plugin enabled");
180 }
181 catch (Exception e)
182 {
183 m_log.ErrorFormat("[FreeSwitchVoice]: plugin initialization failed: {0} {1}", e.Message, e.StackTrace);
184 return;
185 }
186  
187 // This here is a region module trying to make a global setting.
188 // Not really a good idea but it's Windows only, so I can't test.
189 try
190 {
191 ServicePointManager.ServerCertificateValidationCallback += CustomCertificateValidation;
192 }
193 catch (NotImplementedException)
194 {
195 try
196 {
197 #pragma warning disable 0612, 0618
198 // Mono does not implement the ServicePointManager.ServerCertificateValidationCallback yet! Don't remove this!
199 ServicePointManager.CertificatePolicy = new MonoCert();
200 #pragma warning restore 0612, 0618
201 }
202 catch (Exception)
203 {
204 // COmmented multiline spam log message
205 //m_log.Error("[FreeSwitchVoice]: Certificate validation handler change not supported. You may get ssl certificate validation errors teleporting from your region to some SSL regions.");
206 }
207 }
208 }
209  
210 public void PostInitialise()
211 {
212 }
213  
214 public void AddRegion(Scene scene)
215 {
216 // We generate these like this: The region's external host name
217 // as defined in Regions.ini is a good address to use. It's a
218 // dotted quad (or should be!) and it can reach this host from
219 // a client. The port is grabbed from the region's HTTP server.
220 m_openSimWellKnownHTTPAddress = scene.RegionInfo.ExternalHostName;
221 m_freeSwitchServicePort = MainServer.Instance.Port;
222  
223 if (m_Enabled)
224 {
225 // we need to capture scene in an anonymous method
226 // here as we need it later in the callbacks
227 scene.EventManager.OnRegisterCaps += delegate(UUID agentID, Caps caps)
228 {
229 OnRegisterCaps(scene, agentID, caps);
230 };
231 }
232 }
233  
234 public void RemoveRegion(Scene scene)
235 {
236 }
237  
238 public void RegionLoaded(Scene scene)
239 {
240 if (m_Enabled)
241 {
242 m_log.Info("[FreeSwitchVoice]: registering IVoiceModule with the scene");
243  
244 // register the voice interface for this module, so the script engine can call us
245 scene.RegisterModuleInterface<IVoiceModule>(this);
246 }
247 }
248  
249 public void Close()
250 {
251 }
252  
253 public string Name
254 {
255 get { return "FreeSwitchVoiceModule"; }
256 }
257  
258 public Type ReplaceableInterface
259 {
260 get { return null; }
261 }
262  
263 // <summary>
264 // implementation of IVoiceModule, called by osSetParcelSIPAddress script function
265 // </summary>
266 public void setLandSIPAddress(string SIPAddress,UUID GlobalID)
267 {
268 m_log.DebugFormat("[FreeSwitchVoice]: setLandSIPAddress parcel id {0}: setting sip address {1}",
269 GlobalID, SIPAddress);
270  
271 lock (m_ParcelAddress)
272 {
273 if (m_ParcelAddress.ContainsKey(GlobalID.ToString()))
274 {
275 m_ParcelAddress[GlobalID.ToString()] = SIPAddress;
276 }
277 else
278 {
279 m_ParcelAddress.Add(GlobalID.ToString(), SIPAddress);
280 }
281 }
282 }
283  
284 // <summary>
285 // OnRegisterCaps is invoked via the scene.EventManager
286 // everytime OpenSim hands out capabilities to a client
287 // (login, region crossing). We contribute two capabilities to
288 // the set of capabilities handed back to the client:
289 // ProvisionVoiceAccountRequest and ParcelVoiceInfoRequest.
290 //
291 // ProvisionVoiceAccountRequest allows the client to obtain
292 // the voice account credentials for the avatar it is
293 // controlling (e.g., user name, password, etc).
294 //
295 // ParcelVoiceInfoRequest is invoked whenever the client
296 // changes from one region or parcel to another.
297 //
298 // Note that OnRegisterCaps is called here via a closure
299 // delegate containing the scene of the respective region (see
300 // Initialise()).
301 // </summary>
302 public void OnRegisterCaps(Scene scene, UUID agentID, Caps caps)
303 {
304 m_log.DebugFormat(
305 "[FreeSwitchVoice]: OnRegisterCaps() called with agentID {0} caps {1} in scene {2}",
306 agentID, caps, scene.RegionInfo.RegionName);
307  
308 string capsBase = "/CAPS/" + caps.CapsObjectPath;
309 caps.RegisterHandler(
310 "ProvisionVoiceAccountRequest",
311 new RestStreamHandler(
312 "POST",
313 capsBase + m_provisionVoiceAccountRequestPath,
314 (request, path, param, httpRequest, httpResponse)
315 => ProvisionVoiceAccountRequest(scene, request, path, param, agentID, caps),
316 "ProvisionVoiceAccountRequest",
317 agentID.ToString()));
318  
319 caps.RegisterHandler(
320 "ParcelVoiceInfoRequest",
321 new RestStreamHandler(
322 "POST",
323 capsBase + m_parcelVoiceInfoRequestPath,
324 (request, path, param, httpRequest, httpResponse)
325 => ParcelVoiceInfoRequest(scene, request, path, param, agentID, caps),
326 "ParcelVoiceInfoRequest",
327 agentID.ToString()));
328  
329 //caps.RegisterHandler(
330 // "ChatSessionRequest",
331 // new RestStreamHandler(
332 // "POST",
333 // capsBase + m_chatSessionRequestPath,
334 // (request, path, param, httpRequest, httpResponse)
335 // => ChatSessionRequest(scene, request, path, param, agentID, caps),
336 // "ChatSessionRequest",
337 // agentID.ToString()));
338 }
339  
340 /// <summary>
341 /// Callback for a client request for Voice Account Details
342 /// </summary>
343 /// <param name="scene">current scene object of the client</param>
344 /// <param name="request"></param>
345 /// <param name="path"></param>
346 /// <param name="param"></param>
347 /// <param name="agentID"></param>
348 /// <param name="caps"></param>
349 /// <returns></returns>
350 public string ProvisionVoiceAccountRequest(Scene scene, string request, string path, string param,
351 UUID agentID, Caps caps)
352 {
353 m_log.DebugFormat(
354 "[FreeSwitchVoice][PROVISIONVOICE]: ProvisionVoiceAccountRequest() request: {0}, path: {1}, param: {2}", request, path, param);
355  
356 ScenePresence avatar = scene.GetScenePresence(agentID);
357 if (avatar == null)
358 {
359 System.Threading.Thread.Sleep(2000);
360 avatar = scene.GetScenePresence(agentID);
361  
362 if (avatar == null)
363 return "<llsd>undef</llsd>";
364 }
365 string avatarName = avatar.Name;
366  
367 try
368 {
369 //XmlElement resp;
370 string agentname = "x" + Convert.ToBase64String(agentID.GetBytes());
371 string password = "1234";//temp hack//new UUID(Guid.NewGuid()).ToString().Replace('-','Z').Substring(0,16);
372  
373 // XXX: we need to cache the voice credentials, as
374 // FreeSwitch is later going to come and ask us for
375 // those
376 agentname = agentname.Replace('+', '-').Replace('/', '_');
377  
378 lock (m_UUIDName)
379 {
380 if (m_UUIDName.ContainsKey(agentname))
381 {
382 m_UUIDName[agentname] = avatarName;
383 }
384 else
385 {
386 m_UUIDName.Add(agentname, avatarName);
387 }
388 }
389  
390 // LLSDVoiceAccountResponse voiceAccountResponse =
391 // new LLSDVoiceAccountResponse(agentname, password, m_freeSwitchRealm, "http://etsvc02.hursley.ibm.com/api");
392 LLSDVoiceAccountResponse voiceAccountResponse =
393 new LLSDVoiceAccountResponse(agentname, password, m_freeSwitchRealm,
394 String.Format("http://{0}:{1}{2}/", m_openSimWellKnownHTTPAddress,
395 m_freeSwitchServicePort, m_freeSwitchAPIPrefix));
396  
397 string r = LLSDHelpers.SerialiseLLSDReply(voiceAccountResponse);
398  
399 // m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1}", avatarName, r);
400  
401 return r;
402 }
403 catch (Exception e)
404 {
405 m_log.ErrorFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1}, retry later", avatarName, e.Message);
406 m_log.DebugFormat("[FreeSwitchVoice][PROVISIONVOICE]: avatar \"{0}\": {1} failed", avatarName, e.ToString());
407  
408 return "<llsd>undef</llsd>";
409 }
410 }
411  
412 /// <summary>
413 /// Callback for a client request for ParcelVoiceInfo
414 /// </summary>
415 /// <param name="scene">current scene object of the client</param>
416 /// <param name="request"></param>
417 /// <param name="path"></param>
418 /// <param name="param"></param>
419 /// <param name="agentID"></param>
420 /// <param name="caps"></param>
421 /// <returns></returns>
422 public string ParcelVoiceInfoRequest(Scene scene, string request, string path, string param,
423 UUID agentID, Caps caps)
424 {
425 m_log.DebugFormat(
426 "[FreeSwitchVoice][PARCELVOICE]: ParcelVoiceInfoRequest() on {0} for {1}",
427 scene.RegionInfo.RegionName, agentID);
428  
429 ScenePresence avatar = scene.GetScenePresence(agentID);
430 string avatarName = avatar.Name;
431  
432 // - check whether we have a region channel in our cache
433 // - if not:
434 // create it and cache it
435 // - send it to the client
436 // - send channel_uri: as "sip:regionID@m_sipDomain"
437 try
438 {
439 LLSDParcelVoiceInfoResponse parcelVoiceInfo;
440 string channelUri;
441  
442 if (null == scene.LandChannel)
443 throw new Exception(String.Format("region \"{0}\": avatar \"{1}\": land data not yet available",
444 scene.RegionInfo.RegionName, avatarName));
445  
446 // get channel_uri: check first whether estate
447 // settings allow voice, then whether parcel allows
448 // voice, if all do retrieve or obtain the parcel
449 // voice channel
450 LandData land = scene.GetLandData(avatar.AbsolutePosition);
451  
452 //m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": request: {4}, path: {5}, param: {6}",
453 // scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, request, path, param);
454  
455 // TODO: EstateSettings don't seem to get propagated...
456 // if (!scene.RegionInfo.EstateSettings.AllowVoice)
457 // {
458 // m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": voice not enabled in estate settings",
459 // scene.RegionInfo.RegionName);
460 // channel_uri = String.Empty;
461 // }
462 // else
463  
464 if ((land.Flags & (uint)ParcelFlags.AllowVoiceChat) == 0)
465 {
466 // m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": voice not enabled for parcel",
467 // scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName);
468 channelUri = String.Empty;
469 }
470 else
471 {
472 channelUri = ChannelUri(scene, land);
473 }
474  
475 // fill in our response to the client
476 Hashtable creds = new Hashtable();
477 creds["channel_uri"] = channelUri;
478  
479 parcelVoiceInfo = new LLSDParcelVoiceInfoResponse(scene.RegionInfo.RegionName, land.LocalID, creds);
480 string r = LLSDHelpers.SerialiseLLSDReply(parcelVoiceInfo);
481  
482 // m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": {4}",
483 // scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, r);
484 return r;
485 }
486 catch (Exception e)
487 {
488 m_log.ErrorFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": avatar \"{1}\": {2}, retry later",
489 scene.RegionInfo.RegionName, avatarName, e.Message);
490 m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": avatar \"{1}\": {2} failed",
491 scene.RegionInfo.RegionName, avatarName, e.ToString());
492  
493 return "<llsd>undef</llsd>";
494 }
495 }
496  
497 /// <summary>
498 /// Callback for a client request for ChatSessionRequest
499 /// </summary>
500 /// <param name="scene">current scene object of the client</param>
501 /// <param name="request"></param>
502 /// <param name="path"></param>
503 /// <param name="param"></param>
504 /// <param name="agentID"></param>
505 /// <param name="caps"></param>
506 /// <returns></returns>
507 public string ChatSessionRequest(Scene scene, string request, string path, string param,
508 UUID agentID, Caps caps)
509 {
510 ScenePresence avatar = scene.GetScenePresence(agentID);
511 string avatarName = avatar.Name;
512  
513 m_log.DebugFormat("[FreeSwitchVoice][CHATSESSION]: avatar \"{0}\": request: {1}, path: {2}, param: {3}",
514 avatarName, request, path, param);
515  
516 return "<llsd>true</llsd>";
517 }
518  
519 public Hashtable ForwardProxyRequest(Hashtable request)
520 {
521 m_log.Debug("[PROXYING]: -------------------------------proxying request");
522 Hashtable response = new Hashtable();
523 response["content_type"] = "text/xml";
524 response["str_response_string"] = "";
525 response["int_response_code"] = 200;
526  
527 string forwardaddress = "https://www.bhr.vivox.com/api2/";
528 string body = (string)request["body"];
529 string method = (string) request["http-method"];
530 string contenttype = (string) request["content-type"];
531 string uri = (string) request["uri"];
532 uri = uri.Replace("/api/", "");
533 forwardaddress += uri;
534  
535  
536 string fwdresponsestr = "";
537 int fwdresponsecode = 200;
538 string fwdresponsecontenttype = "text/xml";
539  
540 HttpWebRequest forwardreq = (HttpWebRequest)WebRequest.Create(forwardaddress);
541 forwardreq.Method = method;
542 forwardreq.ContentType = contenttype;
543 forwardreq.KeepAlive = false;
544  
545 if (method == "POST")
546 {
547 byte[] contentreq = Util.UTF8.GetBytes(body);
548 forwardreq.ContentLength = contentreq.Length;
549 Stream reqStream = forwardreq.GetRequestStream();
550 reqStream.Write(contentreq, 0, contentreq.Length);
551 reqStream.Close();
552 }
553  
554 using (HttpWebResponse fwdrsp = (HttpWebResponse)forwardreq.GetResponse())
555 {
556 Encoding encoding = Util.UTF8;
557  
558 using (Stream s = fwdrsp.GetResponseStream())
559 {
560 using (StreamReader fwdresponsestream = new StreamReader(s))
561 {
562 fwdresponsestr = fwdresponsestream.ReadToEnd();
563 fwdresponsecontenttype = fwdrsp.ContentType;
564 fwdresponsecode = (int)fwdrsp.StatusCode;
565 }
566 }
567 }
568  
569 response["content_type"] = fwdresponsecontenttype;
570 response["str_response_string"] = fwdresponsestr;
571 response["int_response_code"] = fwdresponsecode;
572  
573 return response;
574 }
575  
576 public Hashtable FreeSwitchSLVoiceGetPreloginHTTPHandler(Hashtable request)
577 {
578 m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceGetPreloginHTTPHandler called");
579  
580 Hashtable response = new Hashtable();
581 response["content_type"] = "text/xml";
582 response["keepalive"] = false;
583  
584 response["str_response_string"] = String.Format(
585 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" +
586 "<VCConfiguration>\r\n"+
587 "<DefaultRealm>{0}</DefaultRealm>\r\n" +
588 "<DefaultSIPProxy>{1}</DefaultSIPProxy>\r\n"+
589 "<DefaultAttemptUseSTUN>{2}</DefaultAttemptUseSTUN>\r\n"+
590 "<DefaultEchoServer>{3}</DefaultEchoServer>\r\n"+
591 "<DefaultEchoPort>{4}</DefaultEchoPort>\r\n"+
592 "<DefaultWellKnownIP>{5}</DefaultWellKnownIP>\r\n"+
593 "<DefaultTimeout>{6}</DefaultTimeout>\r\n"+
594 "<UrlResetPassword>{7}</UrlResetPassword>\r\n"+
595 "<UrlPrivacyNotice>{8}</UrlPrivacyNotice>\r\n"+
596 "<UrlEulaNotice/>\r\n"+
597 "<App.NoBottomLogo>false</App.NoBottomLogo>\r\n"+
598 "</VCConfiguration>",
599 m_freeSwitchRealm, m_freeSwitchSIPProxy, m_freeSwitchAttemptUseSTUN,
600 m_freeSwitchEchoServer, m_freeSwitchEchoPort,
601 m_freeSwitchDefaultWellKnownIP, m_freeSwitchDefaultTimeout,
602 m_freeSwitchUrlResetPassword, "");
603  
604 response["int_response_code"] = 200;
605  
606 //m_log.DebugFormat("[FreeSwitchVoice] FreeSwitchSLVoiceGetPreloginHTTPHandler return {0}",response["str_response_string"]);
607 return response;
608 }
609  
610 public Hashtable FreeSwitchSLVoiceBuddyHTTPHandler(Hashtable request)
611 {
612 m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceBuddyHTTPHandler called");
613  
614 Hashtable response = new Hashtable();
615 response["int_response_code"] = 200;
616 response["str_response_string"] = string.Empty;
617 response["content-type"] = "text/xml";
618  
619 Hashtable requestBody = ParseRequestBody((string)request["body"]);
620  
621 if (!requestBody.ContainsKey("auth_token"))
622 return response;
623  
624 string auth_token = (string)requestBody["auth_token"];
625 //string[] auth_tokenvals = auth_token.Split(':');
626 //string username = auth_tokenvals[0];
627 int strcount = 0;
628  
629 string[] ids = new string[strcount];
630  
631 int iter = -1;
632 lock (m_UUIDName)
633 {
634 strcount = m_UUIDName.Count;
635 ids = new string[strcount];
636 foreach (string s in m_UUIDName.Keys)
637 {
638 iter++;
639 ids[iter] = s;
640 }
641 }
642 StringBuilder resp = new StringBuilder();
643 resp.Append("<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?><response xmlns=\"http://www.vivox.com\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation= \"/xsd/buddy_list.xsd\">");
644  
645 resp.Append(string.Format(@"<level0>
646 <status>OK</status>
647 <cookie_name>lib_session</cookie_name>
648 <cookie>{0}</cookie>
649 <auth_token>{0}</auth_token>
650 <body>
651 <buddies>",auth_token));
652 /*
653 <cookie_name>lib_session</cookie_name>
654 <cookie>{0}:{1}:9303959503950::</cookie>
655 <auth_token>{0}:{1}:9303959503950::</auth_token>
656 */
657 for (int i=0;i<ids.Length;i++)
658 {
659 DateTime currenttime = DateTime.Now;
660 string dt = currenttime.ToString("yyyy-MM-dd HH:mm:ss.0zz");
661 resp.Append(
662 string.Format(@"<level3>
663 <bdy_id>{1}</bdy_id>
664 <bdy_data></bdy_data>
665 <bdy_uri>sip:{0}@{2}</bdy_uri>
666 <bdy_nickname>{0}</bdy_nickname>
667 <bdy_username>{0}</bdy_username>
668 <bdy_domain>{2}</bdy_domain>
669 <bdy_status>A</bdy_status>
670 <modified_ts>{3}</modified_ts>
671 <b2g_group_id></b2g_group_id>
672 </level3>", ids[i], i ,m_freeSwitchRealm, dt));
673 }
674  
675 resp.Append("</buddies><groups></groups></body></level0></response>");
676  
677 response["str_response_string"] = resp.ToString();
678 // Regex normalizeEndLines = new Regex(@"(\r\n|\n)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline);
679 //
680 // m_log.DebugFormat(
681 // "[FREESWITCH]: FreeSwitchSLVoiceBuddyHTTPHandler() response {0}",
682 // normalizeEndLines.Replace((string)response["str_response_string"],""));
683  
684 return response;
685 }
686  
687 public Hashtable FreeSwitchSLVoiceWatcherHTTPHandler(Hashtable request)
688 {
689 m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceWatcherHTTPHandler called");
690  
691 Hashtable response = new Hashtable();
692 response["int_response_code"] = 200;
693 response["content-type"] = "text/xml";
694  
695 Hashtable requestBody = ParseRequestBody((string)request["body"]);
696  
697 string auth_token = (string)requestBody["auth_token"];
698 //string[] auth_tokenvals = auth_token.Split(':');
699 //string username = auth_tokenvals[0];
700  
701 StringBuilder resp = new StringBuilder();
702 resp.Append("<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?><response xmlns=\"http://www.vivox.com\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation= \"/xsd/buddy_list.xsd\">");
703  
704 // FIXME: This is enough of a response to stop viewer 2 complaining about a login failure and get voice to work. If we don't
705 // give an OK response, then viewer 2 engages in an continuous viv_signin.php, viv_buddy.php, viv_watcher.php loop
706 // Viewer 1 appeared happy to ignore the lack of reply and still works with this reply.
707 //
708 // However, really we need to fill in whatever watcher data should be here (whatever that is).
709 resp.Append(string.Format(@"<level0>
710 <status>OK</status>
711 <cookie_name>lib_session</cookie_name>
712 <cookie>{0}</cookie>
713 <auth_token>{0}</auth_token>
714 <body/></level0></response>", auth_token));
715  
716 response["str_response_string"] = resp.ToString();
717  
718 // Regex normalizeEndLines = new Regex(@"(\r\n|\n)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline);
719 //
720 // m_log.DebugFormat(
721 // "[FREESWITCH]: FreeSwitchSLVoiceWatcherHTTPHandler() response {0}",
722 // normalizeEndLines.Replace((string)response["str_response_string"],""));
723  
724 return response;
725 }
726  
727 public Hashtable FreeSwitchSLVoiceSigninHTTPHandler(Hashtable request)
728 {
729 m_log.Debug("[FreeSwitchVoice]: FreeSwitchSLVoiceSigninHTTPHandler called");
730 // string requestbody = (string)request["body"];
731 // string uri = (string)request["uri"];
732 // string contenttype = (string)request["content-type"];
733  
734 Hashtable requestBody = ParseRequestBody((string)request["body"]);
735  
736 //string pwd = (string) requestBody["pwd"];
737 string userid = (string) requestBody["userid"];
738  
739 string avatarName = string.Empty;
740 int pos = -1;
741 lock (m_UUIDName)
742 {
743 if (m_UUIDName.ContainsKey(userid))
744 {
745 avatarName = m_UUIDName[userid];
746 foreach (string s in m_UUIDName.Keys)
747 {
748 pos++;
749 if (s == userid)
750 break;
751 }
752 }
753 }
754  
755 //m_log.DebugFormat("[FreeSwitchVoice]: AUTH, URI: {0}, Content-Type:{1}, Body{2}", uri, contenttype,
756 // requestbody);
757 Hashtable response = new Hashtable();
758 response["str_response_string"] = string.Format(@"<response xsi:schemaLocation=""/xsd/signin.xsd"">
759 <level0>
760 <status>OK</status>
761 <body>
762 <code>200</code>
763 <cookie_name>lib_session</cookie_name>
764 <cookie>{0}:{1}:9303959503950::</cookie>
765 <auth_token>{0}:{1}:9303959503950::</auth_token>
766 <primary>1</primary>
767 <account_id>{1}</account_id>
768 <displayname>{2}</displayname>
769 <msg>auth successful</msg>
770 </body>
771 </level0>
772 </response>", userid, pos, avatarName);
773  
774 response["int_response_code"] = 200;
775  
776 // m_log.DebugFormat("[FreeSwitchVoice]: Sending FreeSwitchSLVoiceSigninHTTPHandler response");
777  
778 return response;
779 }
780  
781 public Hashtable ParseRequestBody(string body)
782 {
783 Hashtable bodyParams = new Hashtable();
784 // split string
785 string [] nvps = body.Split(new Char [] {'&'});
786  
787 foreach (string s in nvps)
788 {
789 if (s.Trim() != "")
790 {
791 string [] nvp = s.Split(new Char [] {'='});
792 bodyParams.Add(HttpUtility.UrlDecode(nvp[0]), HttpUtility.UrlDecode(nvp[1]));
793 }
794 }
795  
796 return bodyParams;
797 }
798  
799 private string ChannelUri(Scene scene, LandData land)
800 {
801 string channelUri = null;
802  
803 string landUUID;
804 string landName;
805  
806 // Create parcel voice channel. If no parcel exists, then the voice channel ID is the same
807 // as the directory ID. Otherwise, it reflects the parcel's ID.
808  
809 lock (m_ParcelAddress)
810 {
811 if (m_ParcelAddress.ContainsKey(land.GlobalID.ToString()))
812 {
813 m_log.DebugFormat("[FreeSwitchVoice]: parcel id {0}: using sip address {1}",
814 land.GlobalID, m_ParcelAddress[land.GlobalID.ToString()]);
815 return m_ParcelAddress[land.GlobalID.ToString()];
816 }
817 }
818  
819 if (land.LocalID != 1 && (land.Flags & (uint)ParcelFlags.UseEstateVoiceChan) == 0)
820 {
821 landName = String.Format("{0}:{1}", scene.RegionInfo.RegionName, land.Name);
822 landUUID = land.GlobalID.ToString();
823 m_log.DebugFormat("[FreeSwitchVoice]: Region:Parcel \"{0}\": parcel id {1}: using channel name {2}",
824 landName, land.LocalID, landUUID);
825 }
826 else
827 {
828 landName = String.Format("{0}:{1}", scene.RegionInfo.RegionName, scene.RegionInfo.RegionName);
829 landUUID = scene.RegionInfo.RegionID.ToString();
830 m_log.DebugFormat("[FreeSwitchVoice]: Region:Parcel \"{0}\": parcel id {1}: using channel name {2}",
831 landName, land.LocalID, landUUID);
832 }
833  
834 // slvoice handles the sip address differently if it begins with confctl, hiding it from the user in the friends list. however it also disables
835 // the personal speech indicators as well unless some siren14-3d codec magic happens. we dont have siren143d so we'll settle for the personal speech indicator.
836 channelUri = String.Format("sip:conf-{0}@{1}", "x" + Convert.ToBase64String(Encoding.ASCII.GetBytes(landUUID)), m_freeSwitchRealm);
837  
838 lock (m_ParcelAddress)
839 {
840 if (!m_ParcelAddress.ContainsKey(land.GlobalID.ToString()))
841 {
842 m_ParcelAddress.Add(land.GlobalID.ToString(),channelUri);
843 }
844 }
845  
846 return channelUri;
847 }
848  
849 private static bool CustomCertificateValidation(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
850 {
851 return true;
852 }
853  
854 public Hashtable FreeSwitchConfigHTTPHandler(Hashtable request)
855 {
856 Hashtable response = new Hashtable();
857 response["str_response_string"] = string.Empty;
858 response["content_type"] = "text/plain";
859 response["keepalive"] = false;
860 response["int_response_code"] = 500;
861  
862 Hashtable requestBody = ParseRequestBody((string)request["body"]);
863  
864 string section = (string) requestBody["section"];
865  
866 if (section == "directory")
867 {
868 string eventCallingFunction = (string)requestBody["Event-Calling-Function"];
869 m_log.DebugFormat(
870 "[FreeSwitchVoice]: Received request for config section directory, event calling function '{0}'",
871 eventCallingFunction);
872  
873 response = m_FreeswitchService.HandleDirectoryRequest(requestBody);
874 }
875 else if (section == "dialplan")
876 {
877 m_log.DebugFormat("[FreeSwitchVoice]: Received request for config section dialplan");
878  
879 response = m_FreeswitchService.HandleDialplanRequest(requestBody);
880 }
881 else
882 m_log.WarnFormat("[FreeSwitchVoice]: Unknown section {0} was requested from config.", section);
883  
884 return response;
885 }
886 }
887  
888 public class MonoCert : ICertificatePolicy
889 {
890 #region ICertificatePolicy Members
891  
892 public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
893 {
894 return true;
895 }
896  
897 #endregion
898 }
899 }