LocalLoginService.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  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.Collections.Generic;
  30. using System.Reflection;
  31. using System.Text.RegularExpressions;
  32. using OpenMetaverse;
  33. using log4net;
  34. using OpenSim.Framework;
  35. using OpenSim.Framework.Communications;
  36. using OpenSim.Framework.Communications.Cache;
  37. namespace OpenSim.Region.Communications.Local
  38. {
  39. public delegate void LoginToRegionEvent(ulong regionHandle, Login login);
  40. public class LocalLoginService : LoginService
  41. {
  42. protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  43. protected NetworkServersInfo serversInfo;
  44. protected uint defaultHomeX;
  45. protected uint defaultHomeY;
  46. protected bool authUsers = false;
  47. /// <summary>
  48. /// Used by the login service to make requests to the inventory service.
  49. /// </summary>
  50. protected IInterServiceInventoryServices m_interServiceInventoryService;
  51. /// <summary>
  52. /// Used to make requests to the local regions.
  53. /// </summary>
  54. protected LocalBackEndServices m_gridService;
  55. public event LoginToRegionEvent OnLoginToRegion;
  56. protected LoginToRegionEvent handlerLoginToRegion = null; // OnLoginToRegion;
  57. public LocalLoginService(
  58. UserManagerBase userManager, string welcomeMess,
  59. IInterServiceInventoryServices interServiceInventoryService, LocalBackEndServices gridService,
  60. NetworkServersInfo serversInfo,
  61. bool authenticate, LibraryRootFolder libraryRootFolder)
  62. : base(userManager, libraryRootFolder, welcomeMess)
  63. {
  64. this.serversInfo = serversInfo;
  65. defaultHomeX = this.serversInfo.DefaultHomeLocX;
  66. defaultHomeY = this.serversInfo.DefaultHomeLocY;
  67. authUsers = authenticate;
  68. m_interServiceInventoryService = interServiceInventoryService;
  69. m_gridService = gridService;
  70. OnLoginToRegion += gridService.AddNewSession;
  71. }
  72. public override UserProfileData GetTheUser(string firstname, string lastname)
  73. {
  74. UserProfileData profile = m_userManager.GetUserProfile(firstname, lastname);
  75. if (profile != null)
  76. {
  77. return profile;
  78. }
  79. if (!authUsers)
  80. {
  81. //no current user account so make one
  82. m_log.Info("[LOGIN]: No user account found so creating a new one.");
  83. m_userManager.AddUser(firstname, lastname, "test", "", defaultHomeX, defaultHomeY);
  84. return m_userManager.GetUserProfile(firstname, lastname);
  85. }
  86. return null;
  87. }
  88. public override bool AuthenticateUser(UserProfileData profile, string password)
  89. {
  90. if (!authUsers)
  91. {
  92. //for now we will accept any password in sandbox mode
  93. m_log.Info("[LOGIN]: Authorising user (no actual password check)");
  94. return true;
  95. }
  96. else
  97. {
  98. m_log.Info(
  99. "[LOGIN]: Authenticating " + profile.FirstName + " " + profile.SurName);
  100. if (!password.StartsWith("$1$"))
  101. password = "$1$" + Util.Md5Hash(password);
  102. password = password.Remove(0, 3); //remove $1$
  103. string s = Util.Md5Hash(password + ":" + profile.PasswordSalt);
  104. bool loginresult = (profile.PasswordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase)
  105. || profile.PasswordHash.Equals(password, StringComparison.InvariantCultureIgnoreCase));
  106. return loginresult;
  107. }
  108. }
  109. /// <summary>
  110. /// Customises the login response and fills in missing values.
  111. /// </summary>
  112. /// <param name="response">The existing response</param>
  113. /// <param name="theUser">The user profile</param>
  114. /// <param name="startLocationRequest">The requested start location</param>
  115. public override bool CustomiseResponse(LoginResponse response, UserProfileData theUser, string startLocationRequest)
  116. {
  117. // add active gestures to login-response
  118. AddActiveGestures(response, theUser);
  119. // HomeLocation
  120. RegionInfo homeInfo = null;
  121. // use the homeRegionID if it is stored already. If not, use the regionHandle as before
  122. if (theUser.HomeRegionID != UUID.Zero)
  123. homeInfo = m_gridService.RequestNeighbourInfo(theUser.HomeRegionID);
  124. else
  125. homeInfo = m_gridService.RequestNeighbourInfo(theUser.HomeRegion);
  126. if (homeInfo != null)
  127. {
  128. response.Home =
  129. string.Format(
  130. "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}",
  131. (homeInfo.RegionLocX*Constants.RegionSize),
  132. (homeInfo.RegionLocY*Constants.RegionSize),
  133. theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z,
  134. theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z);
  135. }
  136. else
  137. {
  138. // Emergency mode: Home-region isn't available, so we can't request the region info.
  139. // Use the stored home regionHandle instead.
  140. // NOTE: If the home-region moves, this will be wrong until the users update their user-profile again
  141. ulong regionX = theUser.HomeRegion >> 32;
  142. ulong regionY = theUser.HomeRegion & 0xffffffff;
  143. response.Home =
  144. string.Format(
  145. "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}",
  146. regionX, regionY,
  147. theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z,
  148. theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z);
  149. m_log.InfoFormat("[LOGIN] Home region of user {0} {1} is not available; using computed region position {2} {3}",
  150. theUser.FirstName, theUser.SurName,
  151. regionX, regionY);
  152. }
  153. // StartLocation
  154. RegionInfo regionInfo = null;
  155. if (startLocationRequest == "home")
  156. {
  157. regionInfo = homeInfo;
  158. theUser.CurrentAgent.Position = theUser.HomeLocation;
  159. response.LookAt = "[r" + theUser.HomeLookAt.X.ToString() + ",r" + theUser.HomeLookAt.Y.ToString() + ",r" + theUser.HomeLookAt.Z.ToString() + "]";
  160. }
  161. else if (startLocationRequest == "last")
  162. {
  163. regionInfo = m_gridService.RequestNeighbourInfo(theUser.CurrentAgent.Region);
  164. response.LookAt = "[r" + theUser.CurrentAgent.LookAt.X.ToString() + ",r" + theUser.CurrentAgent.LookAt.Y.ToString() + ",r" + theUser.CurrentAgent.LookAt.Z.ToString() + "]";
  165. }
  166. else
  167. {
  168. Regex reURI = new Regex(@"^uri:(?<region>[^&]+)&(?<x>\d+)&(?<y>\d+)&(?<z>\d+)$");
  169. Match uriMatch = reURI.Match(startLocationRequest);
  170. if (uriMatch == null)
  171. {
  172. m_log.InfoFormat("[LOGIN]: Got Custom Login URL {0}, but can't process it", startLocationRequest);
  173. }
  174. else
  175. {
  176. string region = uriMatch.Groups["region"].ToString();
  177. regionInfo = m_gridService.RequestClosestRegion(region);
  178. if (regionInfo == null)
  179. {
  180. m_log.InfoFormat("[LOGIN]: Got Custom Login URL {0}, can't locate region {1}", startLocationRequest, region);
  181. }
  182. else
  183. {
  184. theUser.CurrentAgent.Position = new Vector3(float.Parse(uriMatch.Groups["x"].Value),
  185. float.Parse(uriMatch.Groups["y"].Value), float.Parse(uriMatch.Groups["x"].Value));
  186. }
  187. }
  188. response.LookAt = "[r0,r1,r0]";
  189. // can be: last, home, safe, url
  190. response.StartLocation = "url";
  191. }
  192. if ((regionInfo != null) && (PrepareLoginToRegion(regionInfo, theUser, response)))
  193. {
  194. return true;
  195. }
  196. // StartLocation not available, send him to a nearby region instead
  197. // regionInfo = m_gridService.RequestClosestRegion("");
  198. //m_log.InfoFormat("[LOGIN]: StartLocation not available sending to region {0}", regionInfo.regionName);
  199. // Send him to default region instead
  200. ulong defaultHandle = (((ulong)defaultHomeX * Constants.RegionSize) << 32) |
  201. ((ulong)defaultHomeY * Constants.RegionSize);
  202. if ((regionInfo != null) && (defaultHandle == regionInfo.RegionHandle))
  203. {
  204. m_log.ErrorFormat("[LOGIN]: Not trying the default region since this is the same as the selected region");
  205. return false;
  206. }
  207. m_log.Error("[LOGIN]: Sending user to default region " + defaultHandle + " instead");
  208. regionInfo = m_gridService.RequestNeighbourInfo(defaultHandle);
  209. // Customise the response
  210. //response.Home =
  211. // string.Format(
  212. // "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}",
  213. // (SimInfo.regionLocX * Constants.RegionSize),
  214. // (SimInfo.regionLocY*Constants.RegionSize),
  215. // theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z,
  216. // theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z);
  217. theUser.CurrentAgent.Position = new Vector3(128,128,0);
  218. response.StartLocation = "safe";
  219. return PrepareLoginToRegion(regionInfo, theUser, response);
  220. }
  221. /// <summary>
  222. /// Add active gestures of the user to the login response.
  223. /// </summary>
  224. /// <param name="response">
  225. /// A <see cref="LoginResponse"/>
  226. /// </param>
  227. /// <param name="theUser">
  228. /// A <see cref="UserProfileData"/>
  229. /// </param>
  230. private void AddActiveGestures(LoginResponse response, UserProfileData theUser)
  231. {
  232. List<InventoryItemBase> gestures = m_interServiceInventoryService.GetActiveGestures(theUser.ID);
  233. m_log.DebugFormat("[LOGIN]: AddActiveGestures, found {0}", gestures == null ? 0 : gestures.Count);
  234. ArrayList list = new ArrayList();
  235. if (gestures != null)
  236. {
  237. foreach (InventoryItemBase gesture in gestures)
  238. {
  239. Hashtable item = new Hashtable();
  240. item["item_id"] = gesture.ID.ToString();
  241. item["asset_id"] = gesture.AssetID.ToString();
  242. list.Add(item);
  243. }
  244. }
  245. response.ActiveGestures = list;
  246. }
  247. /// <summary>
  248. /// Prepare a login to the given region. This involves both telling the region to expect a connection
  249. /// and appropriately customising the response to the user.
  250. /// </summary>
  251. /// <param name="sim"></param>
  252. /// <param name="user"></param>
  253. /// <param name="response"></param>
  254. /// <returns>true if the region was successfully contacted, false otherwise</returns>
  255. protected bool PrepareLoginToRegion(RegionInfo regionInfo, UserProfileData user, LoginResponse response)
  256. {
  257. response.SimAddress = regionInfo.ExternalEndPoint.Address.ToString();
  258. response.SimPort = (uint)regionInfo.ExternalEndPoint.Port;
  259. response.RegionX = regionInfo.RegionLocX;
  260. response.RegionY = regionInfo.RegionLocY;
  261. string capsPath = Util.GetRandomCapsPath();
  262. // Don't use the following! It Fails for logging into any region not on the same port as the http server!
  263. // Kept here so it doesn't happen again!
  264. // response.SeedCapability = regionInfo.ServerURI + "/CAPS/" + capsPath + "0000/";
  265. string seedcap = "http://";
  266. if (serversInfo.HttpUsesSSL)
  267. {
  268. seedcap = "https://" + serversInfo.HttpSSLCN + ":" + serversInfo.httpSSLPort + "/CAPS/" + capsPath + "0000/";
  269. }
  270. else
  271. {
  272. seedcap = "http://" + regionInfo.ExternalHostName + ":" + serversInfo.HttpListenerPort + "/CAPS/" + capsPath + "0000/";
  273. }
  274. response.SeedCapability = seedcap; //regionInfo.ExternalEndPoint.Address.ToString() + ":" + regionInfo.HttpPort + "/CAPS/" + capsPath + "0000/";
  275. // Notify the target of an incoming user
  276. m_log.InfoFormat(
  277. "[LOGIN]: Telling {0} @ {1},{2} ({3}) to prepare for client connection",
  278. regionInfo.RegionName, response.RegionX, response.RegionY, regionInfo.ServerURI);
  279. // Update agent with target sim
  280. user.CurrentAgent.Region = regionInfo.RegionID;
  281. user.CurrentAgent.Handle = regionInfo.RegionHandle;
  282. // Prepare notification
  283. Login loginParams = new Login();
  284. loginParams.Session = user.CurrentAgent.SessionID;
  285. loginParams.SecureSession = user.CurrentAgent.SecureSessionID;
  286. loginParams.First = user.FirstName;
  287. loginParams.Last = user.SurName;
  288. loginParams.Agent = user.ID;
  289. loginParams.CircuitCode = Convert.ToUInt32(response.CircuitCode);
  290. loginParams.StartPos = user.CurrentAgent.Position;
  291. loginParams.CapsPath = capsPath;
  292. if (m_gridService.RegionLoginsEnabled)
  293. {
  294. handlerLoginToRegion = OnLoginToRegion;
  295. handlerLoginToRegion(user.CurrentAgent.Handle, loginParams);
  296. return true;
  297. }
  298. return false;
  299. }
  300. // See LoginService
  301. protected override InventoryData GetInventorySkeleton(UUID userID)
  302. {
  303. List<InventoryFolderBase> folders = m_interServiceInventoryService.GetInventorySkeleton(userID);
  304. // If we have user auth but no inventory folders for some reason, create a new set of folders.
  305. if (null == folders || 0 == folders.Count)
  306. {
  307. m_interServiceInventoryService.CreateNewUserInventory(userID);
  308. folders = m_interServiceInventoryService.GetInventorySkeleton(userID);
  309. }
  310. UUID rootID = UUID.Zero;
  311. ArrayList AgentInventoryArray = new ArrayList();
  312. Hashtable TempHash;
  313. foreach (InventoryFolderBase InvFolder in folders)
  314. {
  315. if (InvFolder.ParentID == UUID.Zero)
  316. {
  317. rootID = InvFolder.ID;
  318. }
  319. TempHash = new Hashtable();
  320. TempHash["name"] = InvFolder.Name;
  321. TempHash["parent_id"] = InvFolder.ParentID.ToString();
  322. TempHash["version"] = (Int32) InvFolder.Version;
  323. TempHash["type_default"] = (Int32) InvFolder.Type;
  324. TempHash["folder_id"] = InvFolder.ID.ToString();
  325. AgentInventoryArray.Add(TempHash);
  326. }
  327. return new InventoryData(AgentInventoryArray, rootID);
  328. }
  329. public override void LogOffUser(UserProfileData theUser, string message)
  330. {
  331. RegionInfo SimInfo;
  332. try
  333. {
  334. SimInfo = this.m_gridService.RequestNeighbourInfo(theUser.CurrentAgent.Handle);
  335. if (SimInfo == null)
  336. {
  337. m_log.Error("[LOCAL LOGIN]: Region user was in isn't currently logged in");
  338. return;
  339. }
  340. }
  341. catch (Exception)
  342. {
  343. m_log.Error("[LOCAL LOGIN]: Unable to look up region to log user off");
  344. return;
  345. }
  346. m_gridService.TriggerLogOffUser(SimInfo.RegionHandle, theUser.ID, theUser.CurrentAgent.SecureSessionID, "Logging you off");
  347. }
  348. }
  349. }