AsteriskVoiceModule.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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 OpenSim 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. using System;
  28. using System.Collections;
  29. using System.Reflection;
  30. using OpenMetaverse;
  31. using log4net;
  32. using Nini.Config;
  33. using Nwc.XmlRpc;
  34. using OpenSim.Framework;
  35. using OpenSim.Framework.Communications.Cache;
  36. using OpenSim.Framework.Communications.Capabilities;
  37. using OpenSim.Framework.Servers;
  38. using OpenSim.Region.Environment.Interfaces;
  39. using OpenSim.Region.Environment.Scenes;
  40. using Caps=OpenSim.Framework.Communications.Capabilities.Caps;
  41. namespace OpenSim.Region.Environment.Modules.Avatar.Voice.AsterixVoice
  42. {
  43. public class AsteriskVoiceModule : IRegionModule
  44. {
  45. private static readonly ILog m_log =
  46. LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  47. private static readonly string m_parcelVoiceInfoRequestPath = "0007/";
  48. private static readonly string m_provisionVoiceAccountRequestPath = "0008/";
  49. private string m_asterisk;
  50. private string m_asterisk_password;
  51. private string m_asterisk_salt;
  52. private int m_asterisk_timeout;
  53. private string m_confDomain;
  54. private IConfig m_config;
  55. private Scene m_scene;
  56. private string m_sipDomain;
  57. #region IRegionModule Members
  58. public void Initialise(Scene scene, IConfigSource config)
  59. {
  60. m_scene = scene;
  61. m_config = config.Configs["AsteriskVoice"];
  62. if (null == m_config)
  63. {
  64. m_log.Info("[ASTERISKVOICE] no config found, plugin disabled");
  65. return;
  66. }
  67. if (!m_config.GetBoolean("enabled", false))
  68. {
  69. m_log.Info("[ASTERISKVOICE] plugin disabled by configuration");
  70. return;
  71. }
  72. m_log.Info("[ASTERISKVOICE] plugin enabled");
  73. try
  74. {
  75. m_sipDomain = m_config.GetString("sip_domain", String.Empty);
  76. m_log.InfoFormat("[ASTERISKVOICE] using SIP domain {0}", m_sipDomain);
  77. m_confDomain = m_config.GetString("conf_domain", String.Empty);
  78. m_log.InfoFormat("[ASTERISKVOICE] using conf domain {0}", m_confDomain);
  79. m_asterisk = m_config.GetString("asterisk_frontend", String.Empty);
  80. m_asterisk_password = m_config.GetString("asterisk_password", String.Empty);
  81. m_asterisk_timeout = m_config.GetInt("asterisk_timeout", 3000);
  82. m_asterisk_salt = m_config.GetString("asterisk_salt", "Wuffwuff");
  83. if (String.IsNullOrEmpty(m_asterisk)) throw new Exception("missing asterisk_frontend config parameter");
  84. if (String.IsNullOrEmpty(m_asterisk_password)) throw new Exception("missing asterisk_password config parameter");
  85. m_log.InfoFormat("[ASTERISKVOICE] using asterisk front end {0}", m_asterisk);
  86. scene.EventManager.OnRegisterCaps += OnRegisterCaps;
  87. }
  88. catch (Exception e)
  89. {
  90. m_log.ErrorFormat("[ASTERISKVOICE] plugin initialization failed: {0}", e.Message);
  91. m_log.DebugFormat("[ASTERISKVOICE] plugin initialization failed: {0}", e.ToString());
  92. return;
  93. }
  94. }
  95. public void PostInitialise()
  96. {
  97. }
  98. public void Close()
  99. {
  100. }
  101. public string Name
  102. {
  103. get { return "AsteriskVoiceModule"; }
  104. }
  105. public bool IsSharedModule
  106. {
  107. get { return false; }
  108. }
  109. #endregion
  110. public void OnRegisterCaps(UUID agentID, Caps caps)
  111. {
  112. m_log.DebugFormat("[ASTERISKVOICE] OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
  113. string capsBase = "/CAPS/" + caps.CapsObjectPath;
  114. caps.RegisterHandler("ParcelVoiceInfoRequest",
  115. new RestStreamHandler("POST", capsBase + m_parcelVoiceInfoRequestPath,
  116. delegate(string request, string path, string param,
  117. OSHttpRequest httpRequest, OSHttpResponse httpResponse)
  118. {
  119. return ParcelVoiceInfoRequest(request, path, param,
  120. agentID, caps);
  121. }));
  122. caps.RegisterHandler("ProvisionVoiceAccountRequest",
  123. new RestStreamHandler("POST", capsBase + m_provisionVoiceAccountRequestPath,
  124. delegate(string request, string path, string param,
  125. OSHttpRequest httpRequest, OSHttpResponse httpResponse)
  126. {
  127. return ProvisionVoiceAccountRequest(request, path, param,
  128. agentID, caps);
  129. }));
  130. }
  131. /// <summary>
  132. /// Callback for a client request for ParcelVoiceInfo
  133. /// </summary>
  134. /// <param name="request"></param>
  135. /// <param name="path"></param>
  136. /// <param name="param"></param>
  137. /// <param name="agentID"></param>
  138. /// <param name="caps"></param>
  139. /// <returns></returns>
  140. public string ParcelVoiceInfoRequest(string request, string path, string param,
  141. UUID agentID, Caps caps)
  142. {
  143. // we need to do:
  144. // - send channel_uri: as "sip:regionID@m_sipDomain"
  145. try
  146. {
  147. m_log.DebugFormat("[ASTERISKVOICE][PARCELVOICE]: request: {0}, path: {1}, param: {2}",
  148. request, path, param);
  149. // setup response to client
  150. Hashtable creds = new Hashtable();
  151. creds["channel_uri"] = String.Format("sip:{0}@{1}",
  152. m_scene.RegionInfo.RegionID, m_sipDomain);
  153. string regionName = m_scene.RegionInfo.RegionName;
  154. ScenePresence avatar = m_scene.GetScenePresence(agentID);
  155. if (null == m_scene.LandChannel) throw new Exception("land data not yet available");
  156. LandData land = m_scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y);
  157. LLSDParcelVoiceInfoResponse parcelVoiceInfo =
  158. new LLSDParcelVoiceInfoResponse(regionName, land.LocalID, creds);
  159. string r = LLSDHelpers.SerialiseLLSDReply(parcelVoiceInfo);
  160. // update region on asterisk-opensim frontend
  161. Hashtable requestData = new Hashtable();
  162. requestData["admin_password"] = m_asterisk_password;
  163. requestData["region"] = m_scene.RegionInfo.RegionID.ToString();
  164. if (!String.IsNullOrEmpty(m_confDomain))
  165. {
  166. requestData["region"] += String.Format("@{0}", m_confDomain);
  167. }
  168. ArrayList SendParams = new ArrayList();
  169. SendParams.Add(requestData);
  170. XmlRpcRequest updateAccountRequest = new XmlRpcRequest("region_update", SendParams);
  171. XmlRpcResponse updateAccountResponse = updateAccountRequest.Send(m_asterisk, m_asterisk_timeout);
  172. Hashtable responseData = (Hashtable) updateAccountResponse.Value;
  173. if (!responseData.ContainsKey("success")) throw new Exception("region_update call failed");
  174. bool success = Convert.ToBoolean((string) responseData["success"]);
  175. if (!success) throw new Exception("region_update failed");
  176. m_log.DebugFormat("[ASTERISKVOICE][PARCELVOICE]: {0}", r);
  177. return r;
  178. }
  179. catch (Exception e)
  180. {
  181. m_log.ErrorFormat("[ASTERISKVOICE][CAPS][PARCELVOICE]: {0}, retry later", e.Message);
  182. m_log.DebugFormat("[ASTERISKVOICE][CAPS][PARCELVOICE]: {0} failed", e.ToString());
  183. return "<llsd>undef</llsd>";
  184. }
  185. }
  186. /// <summary>
  187. /// Callback for a client request for Voice Account Details
  188. /// </summary>
  189. /// <param name="request"></param>
  190. /// <param name="path"></param>
  191. /// <param name="param"></param>
  192. /// <param name="agentID"></param>
  193. /// <param name="caps"></param>
  194. /// <returns></returns>
  195. public string ProvisionVoiceAccountRequest(string request, string path, string param,
  196. UUID agentID, Caps caps)
  197. {
  198. // we need to
  199. // - get user data from UserProfileCacheService
  200. // - generate nonce for user voice account password
  201. // - issue XmlRpc request to asterisk opensim front end:
  202. // + user: base 64 encoded user name (otherwise SL
  203. // client is unhappy)
  204. // + password: nonce
  205. // - the XmlRpc call to asteris-opensim was successful:
  206. // send account details back to client
  207. try
  208. {
  209. m_log.DebugFormat("[ASTERISKVOICE][PROVISIONVOICE]: request: {0}, path: {1}, param: {2}",
  210. request, path, param);
  211. // get user data & prepare voice account response
  212. string voiceUser = "x" + Convert.ToBase64String(agentID.GetBytes());
  213. voiceUser = voiceUser.Replace('+', '-').Replace('/', '_');
  214. CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(agentID);
  215. if (null == userInfo) throw new Exception("cannot get user details");
  216. // we generate a nonce everytime
  217. string voicePassword = "$1$" + Util.Md5Hash(DateTime.UtcNow.ToLongTimeString() + m_asterisk_salt);
  218. LLSDVoiceAccountResponse voiceAccountResponse =
  219. new LLSDVoiceAccountResponse(voiceUser, voicePassword);
  220. string r = LLSDHelpers.SerialiseLLSDReply(voiceAccountResponse);
  221. m_log.DebugFormat("[CAPS][PROVISIONVOICE]: {0}", r);
  222. // update user account on asterisk frontend
  223. Hashtable requestData = new Hashtable();
  224. requestData["admin_password"] = m_asterisk_password;
  225. requestData["username"] = voiceUser;
  226. if (!String.IsNullOrEmpty(m_sipDomain))
  227. {
  228. requestData["username"] += String.Format("@{0}", m_sipDomain);
  229. }
  230. requestData["password"] = voicePassword;
  231. ArrayList SendParams = new ArrayList();
  232. SendParams.Add(requestData);
  233. XmlRpcRequest updateAccountRequest = new XmlRpcRequest("account_update", SendParams);
  234. XmlRpcResponse updateAccountResponse = updateAccountRequest.Send(m_asterisk, m_asterisk_timeout);
  235. Hashtable responseData = (Hashtable) updateAccountResponse.Value;
  236. if (!responseData.ContainsKey("success")) throw new Exception("account_update call failed");
  237. bool success = Convert.ToBoolean((string) responseData["success"]);
  238. if (!success) throw new Exception("account_update failed");
  239. return r;
  240. }
  241. catch (Exception e)
  242. {
  243. m_log.ErrorFormat("[ASTERISKVOICE][CAPS][PROVISIONVOICE]: {0}, retry later", e.Message);
  244. m_log.DebugFormat("[ASTERISKVOICE][CAPS][PROVISIONVOICE]: {0} failed", e.ToString());
  245. return "<llsd>undef</llsd>";
  246. }
  247. }
  248. }
  249. }