From c0ccfeebe78dc1d0136f86e5f0e9562c4804cfa9 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 21 Nov 2010 20:59:01 +0000 Subject: [PATCH 1/2] Start implementing Freeswitch in ROBUST --- .../Freeswitch/FreeswitchServerConnector.cs | 46 +++++++++++- .../Freeswitch/FreeswitchServerGetHandler.cs | 72 ------------------- .../FreeswitchService/FreeswitchService.cs | 12 +++- .../FreeswitchServiceBase.cs | 36 +++++++++- .../Services/Interfaces/IFreeswitchService.cs | 4 +- bin/Robust.ini.example | 13 ++++ 6 files changed, 106 insertions(+), 77 deletions(-) delete mode 100644 OpenSim/Server/Handlers/Freeswitch/FreeswitchServerGetHandler.cs diff --git a/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerConnector.cs b/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerConnector.cs index 07bafc8c01..150df45cb2 100644 --- a/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerConnector.cs +++ b/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerConnector.cs @@ -26,18 +26,25 @@ */ using System; +using System.Collections; +using System.Web; +using System.Reflection; using Nini.Config; using OpenSim.Server.Base; using OpenSim.Services.Interfaces; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Server.Handlers.Base; +using log4net; namespace OpenSim.Server.Handlers.Freeswitch { public class FreeswitchServerConnector : ServiceConnector { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IFreeswitchService m_FreeswitchService; private string m_ConfigName = "FreeswitchService"; + protected readonly string m_freeSwitchAPIPrefix = "/fsapi"; public FreeswitchServerConnector(IConfigSource config, IHttpServer server, string configName) : base(config, server, configName) @@ -59,7 +66,44 @@ namespace OpenSim.Server.Handlers.Freeswitch m_FreeswitchService = ServerUtils.LoadPlugin(freeswitchService, args); - server.AddStreamHandler(new FreeswitchServerGetHandler(m_FreeswitchService)); + server.AddHTTPHandler(String.Format("{0}/freeswitch-config", m_freeSwitchAPIPrefix), FreeSwitchConfigHTTPHandler); + } + + public Hashtable FreeSwitchConfigHTTPHandler(Hashtable request) + { + Hashtable response = new Hashtable(); + response["str_response_string"] = string.Empty; + + Hashtable requestBody = ParseRequestBody((string) request["body"]); + + string section = (string) requestBody["section"]; + + if (section == "directory") + response = m_FreeswitchService.HandleDirectoryRequest(requestBody); + else if (section == "dialplan") + response = m_FreeswitchService.HandleDialplanRequest(requestBody); + else + m_log.WarnFormat("[FreeSwitchVoice]: section was {0}", section); + + return response; + } + + private Hashtable ParseRequestBody(string body) + { + Hashtable bodyParams = new Hashtable(); + // split string + string [] nvps = body.Split(new Char [] {'&'}); + + foreach (string s in nvps) + { + if (s.Trim() != "") + { + string [] nvp = s.Split(new Char [] {'='}); + bodyParams.Add(HttpUtility.UrlDecode(nvp[0]), HttpUtility.UrlDecode(nvp[1])); + } + } + + return bodyParams; } } } diff --git a/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerGetHandler.cs b/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerGetHandler.cs deleted file mode 100644 index 8b41742916..0000000000 --- a/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerGetHandler.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using Nini.Config; -using log4net; -using System; -using System.IO; -using System.Reflection; -using System.Net; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.Serialization; -using OpenSim.Server.Base; -using OpenSim.Services.Interfaces; -using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; - -namespace OpenSim.Server.Handlers.Freeswitch -{ - public class FreeswitchServerGetHandler : BaseStreamHandler - { - // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - //private IFreeswitchService m_FreeswitchService; - - public FreeswitchServerGetHandler(IFreeswitchService service) : - base("GET", "/api") - { - //m_FreeswitchService = service; - } - - public override byte[] Handle(string path, Stream request, - OSHttpRequest httpRequest, OSHttpResponse httpResponse) - { - byte[] result = new byte[0]; - - string[] p = SplitParams(path); - - if (p.Length == 0) - return result; - - // Process web request - - return result; - } - } -} diff --git a/OpenSim/Services/FreeswitchService/FreeswitchService.cs b/OpenSim/Services/FreeswitchService/FreeswitchService.cs index 0a3830060e..109ef9b81d 100644 --- a/OpenSim/Services/FreeswitchService/FreeswitchService.cs +++ b/OpenSim/Services/FreeswitchService/FreeswitchService.cs @@ -33,19 +33,27 @@ using OpenSim.Framework; using OpenSim.Data; using OpenSim.Services.Interfaces; using OpenMetaverse; +using System.Collections; namespace OpenSim.Services.FreeswitchService { public class FreeswitchService : FreeswitchServiceBase, IFreeswitchService { - //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public FreeswitchService(IConfigSource config) : base(config) { // Perform initilialization here } + public Hashtable HandleDirectoryRequest(Hashtable requestBody) + { + return new Hashtable(); + } - // Implement IFreeswitchService here + public Hashtable HandleDialplanRequest(Hashtable requestBody) + { + return new Hashtable(); + } } } diff --git a/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs b/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs index 83fecef847..ebbb1b06ec 100644 --- a/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs +++ b/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs @@ -31,11 +31,28 @@ using Nini.Config; using OpenSim.Framework; using OpenSim.Services.Interfaces; using OpenSim.Services.Base; +using log4net; namespace OpenSim.Services.FreeswitchService { public class FreeswitchServiceBase : ServiceBase { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_freeSwitchRealm; + protected string m_freeSwitchSIPProxy; + protected bool m_freeSwitchAttemptUseSTUN = false; + protected string m_freeSwitchEchoServer; + protected int m_freeSwitchEchoPort = 50505; + protected string m_freeSwitchDefaultWellKnownIP; + protected int m_freeSwitchDefaultTimeout = 5000; + protected string m_freeSwitchContext = "default"; + protected string m_freeSwitchServerUser = "freeswitch"; + protected string m_freeSwitchServerPass = "password"; + protected readonly string m_freeSwitchAPIPrefix = "/fsapi"; + + protected bool m_Enabled = false; + public FreeswitchServiceBase(IConfigSource config) : base(config) { // @@ -44,7 +61,24 @@ namespace OpenSim.Services.FreeswitchService IConfig freeswitchConfig = config.Configs["FreeswitchService"]; if (freeswitchConfig != null) { - // Read config here !! + m_freeSwitchDefaultWellKnownIP = freeswitchConfig.GetString("ServerAddress", String.Empty); + if (m_freeSwitchDefaultWellKnownIP == String.Empty) + { + m_log.Error("[FREESWITCH]: No FreeswitchServerAddress given, can't continue"); + return; + } + + m_freeSwitchRealm = freeswitchConfig.GetString("Realm", m_freeSwitchDefaultWellKnownIP); + m_freeSwitchSIPProxy = freeswitchConfig.GetString("SIPProxy", m_freeSwitchDefaultWellKnownIP + ":5060"); + m_freeSwitchEchoServer = freeswitchConfig.GetString("EchoServer", m_freeSwitchDefaultWellKnownIP); + m_freeSwitchEchoPort = freeswitchConfig.GetInt("EchoPort", m_freeSwitchEchoPort); + m_freeSwitchAttemptUseSTUN = freeswitchConfig.GetBoolean("AttemptSTUN", false); // This may not work + m_freeSwitchDefaultTimeout = freeswitchConfig.GetInt("DefaultTimeout", m_freeSwitchDefaultTimeout); + m_freeSwitchContext = freeswitchConfig.GetString("Context", m_freeSwitchContext); + m_freeSwitchServerUser = freeswitchConfig.GetString("UserName", m_freeSwitchServerUser); + m_freeSwitchServerPass = freeswitchConfig.GetString("Password", m_freeSwitchServerPass); + + m_Enabled = true; } } } diff --git a/OpenSim/Services/Interfaces/IFreeswitchService.cs b/OpenSim/Services/Interfaces/IFreeswitchService.cs index d1f635b11d..2b3ffeb8a4 100644 --- a/OpenSim/Services/Interfaces/IFreeswitchService.cs +++ b/OpenSim/Services/Interfaces/IFreeswitchService.cs @@ -27,11 +27,13 @@ using System; using OpenSim.Framework; +using System.Collections; namespace OpenSim.Services.Interfaces { public interface IFreeswitchService { - // Place anything the connector eeds to access here! + Hashtable HandleDirectoryRequest(Hashtable requestBody); + Hashtable HandleDialplanRequest(Hashtable requestBody); } } diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index bc87ac286d..14998d4d3f 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -69,6 +69,19 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; * This is the configuration for the freeswitch server in grid mode [FreeswitchService] LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" + ;; IP of your FS server + ; FreeswitchService = 127.0.0.1 + + ;; All other options are - well - optional + ; Realm = 1"27.0.0.1" + ; SIPProxy = 1"27.0.0.1:5060" + ; EchoServer = "127.0.0.1" + ; EchoPort = 50505 + ; AttemptSTUN = "false" + ; DefaultTimeout = 5000 + ; Context = "default" + ; UserName = "freeswitch" + ; Password = "password" ; * This is the new style authentication service. Currently, only MySQL ; * is implemented. "Realm" is the table that is used for user lookup. From ed5f91c638d39938bfca04319434e007fcad90b2 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 21 Nov 2010 21:18:03 +0000 Subject: [PATCH 2/2] Move Dialplan and Directory from the region module to the ROBUST server --- .../FreeswitchService/FreeswitchService.cs | 337 +++++++++++++++++- 1 file changed, 333 insertions(+), 4 deletions(-) diff --git a/OpenSim/Services/FreeswitchService/FreeswitchService.cs b/OpenSim/Services/FreeswitchService/FreeswitchService.cs index 109ef9b81d..b62a65aad5 100644 --- a/OpenSim/Services/FreeswitchService/FreeswitchService.cs +++ b/OpenSim/Services/FreeswitchService/FreeswitchService.cs @@ -26,6 +26,7 @@ */ using System; +using System.Text; using System.Reflection; using Nini.Config; using log4net; @@ -46,14 +47,342 @@ namespace OpenSim.Services.FreeswitchService // Perform initilialization here } - public Hashtable HandleDirectoryRequest(Hashtable requestBody) + public Hashtable HandleDialplanRequest(Hashtable request) { - return new Hashtable(); + m_log.DebugFormat("[FreeSwitchVoice] HandleDialplanRequest called with {0}",request.ToString()); + + Hashtable response = new Hashtable(); + + foreach (DictionaryEntry item in request) + { + m_log.InfoFormat("[FreeSwitchDirectory] requestBody item {0} {1}",item.Key, item.Value); + } + + string requestcontext = (string) request["Hunt-Context"]; + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + + if (m_freeSwitchContext != String.Empty && m_freeSwitchContext != requestcontext) + { + m_log.Debug("[FreeSwitchDirectory] returning empty as it's for another context"); + response["str_response_string"] = ""; + } + else + { + response["str_response_string"] = String.Format(@" + +
+ " + + +/* + + + + + + */ + + @" + + + + + + + + + + + + + + + + + + + + +
+
", m_freeSwitchContext, m_freeSwitchRealm); + } + + return response; } - public Hashtable HandleDialplanRequest(Hashtable requestBody) + public Hashtable HandleDirectoryRequest(Hashtable request) { - return new Hashtable(); + Hashtable response = new Hashtable(); + string domain = (string) request["domain"]; + if (domain != m_freeSwitchRealm) { + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = ""; + } else { + m_log.DebugFormat("[FreeSwitchDirectory] HandleDirectoryRequest called with {0}",request.ToString()); + + // information in the request we might be interested in + + // Request 1 sip_auth for users account + + //Event-Calling-Function=sofia_reg_parse_auth + //Event-Calling-Line-Number=1494 + //action=sip_auth + //sip_user_agent=Vivox-SDK-2.1.3010.6151-Mac%20(Feb-11-2009/16%3A42%3A41) + //sip_auth_username=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) + //sip_auth_realm=9.20.151.43 + //sip_contact_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) + //sip_contact_host=192.168.0.3 // this shouldnt really be a local IP, investigate STUN servers + //sip_to_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D + //sip_to_host=9.20.151.43 + //sip_auth_method=REGISTER + //user=xhZuXKmRpECyr2AARJYyGgg%3D%3D + //domain=9.20.151.43 + //ip=9.167.220.137 // this is the correct IP rather than sip_contact_host above when through a vpn or NAT setup + + foreach (DictionaryEntry item in request) + { + m_log.InfoFormat("[FreeSwitchDirectory] requestBody item {0} {1}", item.Key, item.Value); + } + + string eventCallingFunction = (string) request["Event-Calling-Function"]; + if (eventCallingFunction == null) + { + eventCallingFunction = "sofia_reg_parse_auth"; + } + + if (eventCallingFunction.Length == 0) + { + eventCallingFunction = "sofia_reg_parse_auth"; + } + + if (eventCallingFunction == "sofia_reg_parse_auth") + { + string sipAuthMethod = (string)request["sip_auth_method"]; + + if (sipAuthMethod == "REGISTER") + { + response = HandleRegister(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else if (sipAuthMethod == "INVITE") + { + response = HandleInvite(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else + { + m_log.ErrorFormat("[FreeSwitchVoice] HandleDirectoryRequest unknown sip_auth_method {0}",sipAuthMethod); + response["int_response_code"] = 404; + response["content_type"] = "text/xml"; + response["str_response_string"] = ""; + } + } + else if (eventCallingFunction == "switch_xml_locate_user") + { + response = HandleLocateUser(m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "user_data_function") // gets called when an avatar to avatar call is made + { + response = HandleLocateUser(m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "user_outgoing_channel") + { + response = HandleRegister(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "config_sofia") // happens once on freeswitch startup + { + response = HandleConfigSofia(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "switch_load_network_lists") + { + //response = HandleLoadNetworkLists(request); + response["int_response_code"] = 404; + response["keepalive"] = false; + response["content_type"] = "text/xml"; + response["str_response_string"] = ""; + } + else + { + m_log.ErrorFormat("[FreeSwitchVoice] HandleDirectoryRequest unknown Event-Calling-Function {0}",eventCallingFunction); + response["int_response_code"] = 404; + response["keepalive"] = false; + response["content_type"] = "text/xml"; + response["str_response_string"] = ""; + } + } + return response; + } + + private Hashtable HandleRegister(string Context, string Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory] HandleRegister called"); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string password = "1234"; + string domain = (string) request["domain"]; + string user = (string) request["user"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + ""+ + "\r\n" + + "\r\n" + + "\r\n" + + "
\r\n" + + "
\r\n", + domain , user, password, Context); + + return response; + } + + private Hashtable HandleInvite(string Context, string Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory] HandleInvite called"); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string password = "1234"; + string domain = (string) request["domain"]; + string user = (string) request["user"]; + string sipRequestUser = (string) request["sip_request_user"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + ""+ + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + ""+ + "\r\n" + + "\r\n" + + "\r\n" + + "
\r\n" + + "
\r\n", + domain , user, password,sipRequestUser, Context); + + return response; + } + + private Hashtable HandleLocateUser(String Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory] HandleLocateUser called"); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string domain = (string) request["domain"]; + string user = (string) request["user"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n"+ + "\r\n"+ + ""+ + "\r\n"+ + "\r\n" + + "\r\n" + + "
\r\n" + + "
\r\n", + domain , user); + + return response; + } + + private Hashtable HandleConfigSofia(string Context, string Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory] HandleConfigSofia called"); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string domain = (string) request["domain"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + ""+ + "\r\n" + + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n" + + "
\r\n" + + "
\r\n", + domain, Context); + + return response; } } }