OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
FreeSwitchVoiceModule.cs
Go to the documentation of this file.
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;
53 using System.Text.RegularExpressions;
54 using OpenSim.Server.Base;
55 using OpenSim.Services.Interfaces;
57 
58 namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice
59 {
60  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FreeSwitchVoiceModule")]
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 
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 
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 
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 }
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
void setLandSIPAddress(string SIPAddress, UUID GlobalID)
Set the SIP url to be used by a parcel, this will allow manual setting of a SIP address for a particu...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
string ProvisionVoiceAccountRequest(Scene scene, string request, string path, string param, UUID agentID, Caps caps)
Callback for a client request for Voice Account Details
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
UUID GlobalID
Global ID for the parcel. (3rd Party Integration)
Definition: LandData.cs:327
OpenSim.Framework.Capabilities.Caps Caps
string ChatSessionRequest(Scene scene, string request, string path, string param, UUID agentID, Caps caps)
Callback for a client request for ChatSessionRequest
Details of a Parcel of land
Definition: LandData.cs:47
string ParcelVoiceInfoRequest(Scene scene, string request, string path, string param, UUID agentID, Caps caps)
Callback for a client request for ParcelVoiceInfo
OpenMetaverse.StructuredData.OSDMap OSDMap
Interactive OpenSim region server
Definition: OpenSim.cs:55
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
uint Flags
Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags ...
Definition: LandData.cs:402
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
int LocalID
Internal ID of the parcel. Sometimes the client will try to use this value
Definition: LandData.cs:463
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...