31 using System.Net.Security;
33 using System.Security.Cryptography.X509Certificates;
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Reflection;
40 using OpenMetaverse.StructuredData;
44 using OpenSim.Framework;
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;
60 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"FreeSwitchVoiceModule")]
63 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
66 private static readonly
string m_parcelVoiceInfoRequestPath =
"0207/";
67 private static readonly
string m_provisionVoiceAccountRequestPath =
"0208/";
71 private static bool m_Enabled =
false;
77 private static string m_freeSwitchAPIPrefix;
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;
97 private readonly Dictionary<string, string> m_UUIDName =
new Dictionary<string, string>();
98 private Dictionary<string, string> m_ParcelAddress =
new Dictionary<string, string>();
100 private IConfig m_Config;
106 m_Config = config.Configs[
"FreeSwitchVoice"];
108 if (m_Config == null)
111 if (!m_Config.GetBoolean(
"Enabled",
false))
116 string serviceDll = m_Config.GetString(
"LocalServiceModule",
119 if (serviceDll ==
String.Empty)
121 m_log.Error(
"[FreeSwitchVoice]: No LocalServiceModule named in section FreeSwitchVoice. Not starting.");
128 string jsonConfig = m_FreeswitchService.GetJsonConfig();
130 OSDMap map = (
OSDMap)OSDParser.DeserializeJson(jsonConfig);
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;
143 if (
String.IsNullOrEmpty(m_freeSwitchRealm) ||
144 String.IsNullOrEmpty(m_freeSwitchAPIPrefix))
146 m_log.Error(
"[FreeSwitchVoice]: Freeswitch service mis-configured. Not starting.");
156 MainServer.Instance.AddHTTPHandler(String.Format(
"{0}/viv_get_prelogin.php", m_freeSwitchAPIPrefix),
157 FreeSwitchSLVoiceGetPreloginHTTPHandler);
159 MainServer.Instance.AddHTTPHandler(String.Format(
"{0}/freeswitch-config", m_freeSwitchAPIPrefix), FreeSwitchConfigHTTPHandler);
166 MainServer.Instance.AddHTTPHandler(String.Format(
"{0}/viv_signin.php", m_freeSwitchAPIPrefix),
167 FreeSwitchSLVoiceSigninHTTPHandler);
169 MainServer.Instance.AddHTTPHandler(String.Format(
"{0}/viv_buddy.php", m_freeSwitchAPIPrefix),
170 FreeSwitchSLVoiceBuddyHTTPHandler);
172 MainServer.Instance.AddHTTPHandler(String.Format(
"{0}/viv_watcher.php", m_freeSwitchAPIPrefix),
173 FreeSwitchSLVoiceWatcherHTTPHandler);
175 m_log.InfoFormat(
"[FreeSwitchVoice]: using FreeSwitch server {0}", m_freeSwitchRealm);
179 m_log.Info(
"[FreeSwitchVoice]: plugin enabled");
183 m_log.ErrorFormat(
"[FreeSwitchVoice]: plugin initialization failed: {0} {1}", e.Message, e.StackTrace);
191 ServicePointManager.ServerCertificateValidationCallback += CustomCertificateValidation;
193 catch (NotImplementedException)
197 #pragma warning disable 0612, 0618
199 ServicePointManager.CertificatePolicy =
new MonoCert();
200 #pragma warning restore 0612, 0618
220 m_openSimWellKnownHTTPAddress = scene.RegionInfo.ExternalHostName;
221 m_freeSwitchServicePort = MainServer.Instance.Port;
227 scene.EventManager.OnRegisterCaps += delegate(
UUID agentID,
Caps caps)
229 OnRegisterCaps(scene, agentID, caps);
242 m_log.Info(
"[FreeSwitchVoice]: registering IVoiceModule with the scene");
255 get {
return "FreeSwitchVoiceModule"; }
258 public Type ReplaceableInterface
268 m_log.DebugFormat(
"[FreeSwitchVoice]: setLandSIPAddress parcel id {0}: setting sip address {1}",
269 GlobalID, SIPAddress);
271 lock (m_ParcelAddress)
273 if (m_ParcelAddress.ContainsKey(GlobalID.ToString()))
275 m_ParcelAddress[GlobalID.ToString()] = SIPAddress;
279 m_ParcelAddress.Add(GlobalID.ToString(), SIPAddress);
305 "[FreeSwitchVoice]: OnRegisterCaps() called with agentID {0} caps {1} in scene {2}",
306 agentID, caps, scene.RegionInfo.RegionName);
308 string capsBase =
"/CAPS/" + caps.CapsObjectPath;
309 caps.RegisterHandler(
310 "ProvisionVoiceAccountRequest",
313 capsBase + m_provisionVoiceAccountRequestPath,
314 (request, path, param, httpRequest, httpResponse)
315 => ProvisionVoiceAccountRequest(scene, request, path, param, agentID, caps),
316 "ProvisionVoiceAccountRequest",
317 agentID.ToString()));
319 caps.RegisterHandler(
320 "ParcelVoiceInfoRequest",
323 capsBase + m_parcelVoiceInfoRequestPath,
324 (request, path, param, httpRequest, httpResponse)
325 => ParcelVoiceInfoRequest(scene, request, path, param, agentID, caps),
326 "ParcelVoiceInfoRequest",
327 agentID.ToString()));
351 UUID agentID,
Caps caps)
354 "[FreeSwitchVoice][PROVISIONVOICE]: ProvisionVoiceAccountRequest() request: {0}, path: {1}, param: {2}", request, path, param);
359 System.Threading.Thread.Sleep(2000);
360 avatar = scene.GetScenePresence(agentID);
363 return "<llsd>undef</llsd>";
365 string avatarName = avatar.Name;
370 string agentname =
"x" + Convert.ToBase64String(agentID.GetBytes());
371 string password =
"1234";
376 agentname = agentname.Replace(
'+',
'-').Replace(
'/',
'_');
380 if (m_UUIDName.ContainsKey(agentname))
382 m_UUIDName[agentname] = avatarName;
386 m_UUIDName.Add(agentname, avatarName);
394 String.Format(
"http://{0}:{1}{2}/", m_openSimWellKnownHTTPAddress,
395 m_freeSwitchServicePort, m_freeSwitchAPIPrefix));
397 string r = LLSDHelpers.SerialiseLLSDReply(voiceAccountResponse);
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());
408 return "<llsd>undef</llsd>";
423 UUID agentID,
Caps caps)
426 "[FreeSwitchVoice][PARCELVOICE]: ParcelVoiceInfoRequest() on {0} for {1}",
427 scene.RegionInfo.RegionName, agentID);
430 string avatarName = avatar.Name;
443 throw new Exception(
String.Format(
"region \"{0}\": avatar \"{1}\": land data not yet available",
450 LandData land = scene.GetLandData(avatar.AbsolutePosition);
464 if ((land.Flags & (uint)ParcelFlags.AllowVoiceChat) == 0)
468 channelUri = String.Empty;
472 channelUri = ChannelUri(scene, land);
476 Hashtable creds =
new Hashtable();
477 creds[
"channel_uri"] = channelUri;
480 string r = LLSDHelpers.SerialiseLLSDReply(parcelVoiceInfo);
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());
493 return "<llsd>undef</llsd>";
508 UUID agentID,
Caps caps)
511 string avatarName = avatar.Name;
513 m_log.DebugFormat(
"[FreeSwitchVoice][CHATSESSION]: avatar \"{0}\": request: {1}, path: {2}, param: {3}",
514 avatarName, request, path, param);
516 return "<llsd>true</llsd>";
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;
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;
536 string fwdresponsestr =
"";
537 int fwdresponsecode = 200;
538 string fwdresponsecontenttype =
"text/xml";
540 HttpWebRequest forwardreq = (HttpWebRequest)WebRequest.Create(forwardaddress);
541 forwardreq.Method = method;
542 forwardreq.ContentType = contenttype;
543 forwardreq.KeepAlive =
false;
545 if (method ==
"POST")
547 byte[] contentreq = Util.UTF8.GetBytes(body);
548 forwardreq.ContentLength = contentreq.Length;
549 Stream reqStream = forwardreq.GetRequestStream();
550 reqStream.Write(contentreq, 0, contentreq.Length);
554 using (HttpWebResponse fwdrsp = (HttpWebResponse)forwardreq.GetResponse())
556 Encoding encoding = Util.UTF8;
558 using (Stream s = fwdrsp.GetResponseStream())
560 using (StreamReader fwdresponsestream =
new StreamReader(s))
562 fwdresponsestr = fwdresponsestream.ReadToEnd();
563 fwdresponsecontenttype = fwdrsp.ContentType;
564 fwdresponsecode = (int)fwdrsp.StatusCode;
569 response[
"content_type"] = fwdresponsecontenttype;
570 response[
"str_response_string"] = fwdresponsestr;
571 response[
"int_response_code"] = fwdresponsecode;
580 Hashtable response =
new Hashtable();
581 response[
"content_type"] =
"text/xml";
582 response[
"keepalive"] =
false;
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,
"");
604 response[
"int_response_code"] = 200;
612 m_log.Debug(
"[FreeSwitchVoice]: FreeSwitchSLVoiceBuddyHTTPHandler called");
614 Hashtable response =
new Hashtable();
615 response[
"int_response_code"] = 200;
616 response[
"str_response_string"] = string.Empty;
617 response[
"content-type"] =
"text/xml";
619 Hashtable requestBody = ParseRequestBody((
string)request[
"body"]);
621 if (!requestBody.ContainsKey(
"auth_token"))
624 string auth_token = (string)requestBody[
"auth_token"];
629 string[] ids =
new string[strcount];
634 strcount = m_UUIDName.Count;
635 ids =
new string[strcount];
636 foreach (
string s
in m_UUIDName.Keys)
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\">");
645 resp.Append(string.Format(
@"<level0>
647 <cookie_name>lib_session</cookie_name>
649 <auth_token>{0}</auth_token>
651 <buddies>",auth_token));
657 for (
int i=0;i<ids.Length;i++)
659 DateTime currenttime = DateTime.Now;
660 string dt = currenttime.ToString(
"yyyy-MM-dd HH:mm:ss.0zz");
662 string.Format(
@"<level3>
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));
675 resp.Append(
"</buddies><groups></groups></body></level0></response>");
677 response[
"str_response_string"] = resp.ToString();
689 m_log.Debug(
"[FreeSwitchVoice]: FreeSwitchSLVoiceWatcherHTTPHandler called");
691 Hashtable response =
new Hashtable();
692 response[
"int_response_code"] = 200;
693 response[
"content-type"] =
"text/xml";
695 Hashtable requestBody = ParseRequestBody((
string)request[
"body"]);
697 string auth_token = (string)requestBody[
"auth_token"];
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\">");
709 resp.Append(string.Format(
@"<level0>
711 <cookie_name>lib_session</cookie_name>
713 <auth_token>{0}</auth_token>
714 <body/></level0></response>", auth_token));
716 response[
"str_response_string"] = resp.ToString();
734 Hashtable requestBody = ParseRequestBody((
string)request[
"body"]);
737 string userid = (string) requestBody[
"userid"];
739 string avatarName = string.Empty;
743 if (m_UUIDName.ContainsKey(userid))
745 avatarName = m_UUIDName[userid];
746 foreach (
string s
in m_UUIDName.Keys)
757 Hashtable response =
new Hashtable();
758 response[
"str_response_string"] = string.Format(
@"<response xsi:schemaLocation=""/xsd/signin.xsd"">
763 <cookie_name>lib_session</cookie_name>
764 <cookie>{0}:{1}:9303959503950::</cookie>
765 <auth_token>{0}:{1}:9303959503950::</auth_token>
767 <account_id>{1}</account_id>
768 <displayname>{2}</displayname>
769 <msg>auth successful</msg>
772 </response>", userid, pos, avatarName);
774 response[
"int_response_code"] = 200;
783 Hashtable bodyParams =
new Hashtable();
785 string [] nvps = body.Split(
new Char [] {
'&'});
787 foreach (
string s
in nvps)
791 string [] nvp = s.Split(
new Char [] {
'='});
792 bodyParams.Add(HttpUtility.UrlDecode(nvp[0]), HttpUtility.UrlDecode(nvp[1]));
801 string channelUri = null;
809 lock (m_ParcelAddress)
811 if (m_ParcelAddress.ContainsKey(land.
GlobalID.ToString()))
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()];
819 if (land.
LocalID != 1 && (land.
Flags & (uint)ParcelFlags.UseEstateVoiceChan) == 0)
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);
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);
836 channelUri = String.Format(
"sip:conf-{0}@{1}",
"x" + Convert.ToBase64String(Encoding.ASCII.GetBytes(landUUID)), m_freeSwitchRealm);
838 lock (m_ParcelAddress)
840 if (!m_ParcelAddress.ContainsKey(land.
GlobalID.ToString()))
842 m_ParcelAddress.Add(land.GlobalID.ToString(),channelUri);
849 private static bool CustomCertificateValidation(
object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
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;
862 Hashtable requestBody = ParseRequestBody((
string)request[
"body"]);
864 string section = (string) requestBody[
"section"];
866 if (section ==
"directory")
868 string eventCallingFunction = (string)requestBody[
"Event-Calling-Function"];
870 "[FreeSwitchVoice]: Received request for config section directory, event calling function '{0}'",
871 eventCallingFunction);
873 response = m_FreeswitchService.HandleDirectoryRequest(requestBody);
875 else if (section ==
"dialplan")
877 m_log.DebugFormat(
"[FreeSwitchVoice]: Received request for config section dialplan");
879 response = m_FreeswitchService.HandleDialplanRequest(requestBody);
882 m_log.WarnFormat(
"[FreeSwitchVoice]: Unknown section {0} was requested from config.", section);
890 #region ICertificatePolicy Members
892 public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request,
int certificateProblem)
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...
Hashtable FreeSwitchConfigHTTPHandler(Hashtable request)
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...
Hashtable FreeSwitchSLVoiceSigninHTTPHandler(Hashtable request)
UUID GlobalID
Global ID for the parcel. (3rd Party Integration)
Hashtable FreeSwitchSLVoiceBuddyHTTPHandler(Hashtable request)
OpenSim.Framework.Capabilities.Caps Caps
Hashtable FreeSwitchSLVoiceWatcherHTTPHandler(Hashtable request)
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
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
Hashtable FreeSwitchSLVoiceGetPreloginHTTPHandler(Hashtable request)
Interactive OpenSim region server
Hashtable ParseRequestBody(string body)
Hashtable ForwardProxyRequest(Hashtable request)
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 ...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
void OnRegisterCaps(Scene scene, UUID agentID, Caps caps)
virtual RegionInfo RegionInfo
int LocalID
Internal ID of the parcel. Sometimes the client will try to use this value
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...