UserLoginService.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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 Nwc.XmlRpc;
  35. using OpenSim.Data;
  36. using OpenSim.Framework;
  37. using OpenSim.Framework.Communications;
  38. using OpenSim.Framework.Communications.Cache;
  39. namespace OpenSim.Grid.UserServer
  40. {
  41. public delegate void UserLoggedInAtLocation(UUID agentID, UUID sessionID, UUID RegionID,
  42. ulong regionhandle, float positionX, float positionY, float positionZ,
  43. string firstname, string lastname);
  44. /// <summary>
  45. /// Login service used in grid mode.
  46. /// </summary>
  47. public class UserLoginService : LoginService
  48. {
  49. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  50. protected IInterServiceInventoryServices m_inventoryService;
  51. public event UserLoggedInAtLocation OnUserLoggedInAtLocation;
  52. private UserLoggedInAtLocation handlerUserLoggedInAtLocation;
  53. public UserConfig m_config;
  54. public UserLoginService(
  55. UserManagerBase userManager, IInterServiceInventoryServices inventoryService,
  56. LibraryRootFolder libraryRootFolder,
  57. UserConfig config, string welcomeMess)
  58. : base(userManager, libraryRootFolder, welcomeMess)
  59. {
  60. m_config = config;
  61. m_inventoryService = inventoryService;
  62. }
  63. public void setloginlevel(int level)
  64. {
  65. m_minLoginLevel = level;
  66. m_log.InfoFormat("[GRID]: Login Level set to {0} ", level);
  67. }
  68. public void setwelcometext(string text)
  69. {
  70. m_welcomeMessage = text;
  71. m_log.InfoFormat("[GRID]: Login text set to {0} ", text);
  72. }
  73. public override void LogOffUser(UserProfileData theUser, string message)
  74. {
  75. RegionProfileData SimInfo;
  76. try
  77. {
  78. SimInfo = RegionProfileData.RequestSimProfileData(
  79. theUser.CurrentAgent.Handle, m_config.GridServerURL,
  80. m_config.GridSendKey, m_config.GridRecvKey);
  81. if (SimInfo == null)
  82. {
  83. m_log.Error("[GRID]: Region user was in isn't currently logged in");
  84. return;
  85. }
  86. }
  87. catch (Exception)
  88. {
  89. m_log.Error("[GRID]: Unable to look up region to log user off");
  90. return;
  91. }
  92. // Prepare notification
  93. Hashtable SimParams = new Hashtable();
  94. SimParams["agent_id"] = theUser.ID.ToString();
  95. SimParams["region_secret"] = theUser.CurrentAgent.SecureSessionID.ToString();
  96. //SimParams["region_secret"] = SimInfo.regionSecret;
  97. //m_log.Info(SimInfo.regionSecret);
  98. SimParams["regionhandle"] = theUser.CurrentAgent.Handle.ToString();
  99. SimParams["message"] = message;
  100. ArrayList SendParams = new ArrayList();
  101. SendParams.Add(SimParams);
  102. m_log.InfoFormat(
  103. "[ASSUMED CRASH]: Telling region {0} @ {1},{2} ({3}) that their agent is dead: {4}",
  104. SimInfo.regionName, SimInfo.regionLocX, SimInfo.regionLocY, SimInfo.httpServerURI,
  105. theUser.FirstName + " " + theUser.SurName);
  106. try
  107. {
  108. XmlRpcRequest GridReq = new XmlRpcRequest("logoff_user", SendParams);
  109. XmlRpcResponse GridResp = GridReq.Send(SimInfo.httpServerURI, 6000);
  110. if (GridResp.IsFault)
  111. {
  112. m_log.ErrorFormat(
  113. "[LOGIN]: XMLRPC request for {0} failed, fault code: {1}, reason: {2}, This is likely an old region revision.",
  114. SimInfo.httpServerURI, GridResp.FaultCode, GridResp.FaultString);
  115. }
  116. }
  117. catch (Exception)
  118. {
  119. m_log.Error("[LOGIN]: Error telling region to logout user!");
  120. }
  121. //base.LogOffUser(theUser);
  122. }
  123. /// <summary>
  124. /// Customises the login response and fills in missing values.
  125. /// </summary>
  126. /// <param name="response">The existing response</param>
  127. /// <param name="theUser">The user profile</param>
  128. /// <param name="startLocationRequest">The requested start location</param>
  129. public override bool CustomiseResponse(LoginResponse response, UserProfileData theUser, string startLocationRequest)
  130. {
  131. // add active gestures to login-response
  132. AddActiveGestures(response, theUser);
  133. // HomeLocation
  134. RegionProfileData homeInfo = null;
  135. // use the homeRegionID if it is stored already. If not, use the regionHandle as before
  136. if (theUser.HomeRegionID != UUID.Zero)
  137. homeInfo = RegionProfileData.RequestSimProfileData(theUser.HomeRegionID,
  138. m_config.GridServerURL, m_config.GridSendKey, m_config.GridRecvKey);
  139. else
  140. homeInfo = RegionProfileData.RequestSimProfileData(theUser.HomeRegion,
  141. m_config.GridServerURL, m_config.GridSendKey, m_config.GridRecvKey);
  142. if (homeInfo != null)
  143. {
  144. response.Home =
  145. string.Format(
  146. "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}",
  147. (homeInfo.regionLocX*Constants.RegionSize),
  148. (homeInfo.regionLocY*Constants.RegionSize),
  149. theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z,
  150. theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z);
  151. }
  152. else
  153. {
  154. // Emergency mode: Home-region isn't available, so we can't request the region info.
  155. // Use the stored home regionHandle instead.
  156. // NOTE: If the home-region moves, this will be wrong until the users update their user-profile again
  157. ulong regionX = theUser.HomeRegion >> 32;
  158. ulong regionY = theUser.HomeRegion & 0xffffffff;
  159. response.Home =
  160. string.Format(
  161. "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}",
  162. regionX, regionY,
  163. theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z,
  164. theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z);
  165. m_log.InfoFormat("[LOGIN] Home region of user {0} {1} is not available; using computed region position {2} {3}",
  166. theUser.FirstName, theUser.SurName,
  167. regionX, regionY);
  168. }
  169. // StartLocation
  170. RegionProfileData regionInfo = null;
  171. if (startLocationRequest == "home")
  172. {
  173. regionInfo = homeInfo;
  174. theUser.CurrentAgent.Position = theUser.HomeLocation;
  175. response.LookAt = "[r" + theUser.HomeLookAt.X.ToString() + ",r" + theUser.HomeLookAt.Y.ToString() + ",r" + theUser.HomeLookAt.Z.ToString() + "]";
  176. }
  177. else if (startLocationRequest == "last")
  178. {
  179. regionInfo = RegionProfileData.RequestSimProfileData(theUser.CurrentAgent.Region,
  180. m_config.GridServerURL, m_config.GridSendKey, m_config.GridRecvKey);
  181. response.LookAt = "[r" + theUser.CurrentAgent.LookAt.X.ToString() + ",r" + theUser.CurrentAgent.LookAt.Y.ToString() + ",r" + theUser.CurrentAgent.LookAt.Z.ToString() + "]";
  182. }
  183. else
  184. {
  185. Regex reURI = new Regex(@"^uri:(?<region>[^&]+)&(?<x>\d+)&(?<y>\d+)&(?<z>\d+)$");
  186. Match uriMatch = reURI.Match(startLocationRequest);
  187. if (uriMatch == null)
  188. {
  189. m_log.InfoFormat("[LOGIN]: Got Custom Login URL {0}, but can't process it", startLocationRequest);
  190. }
  191. else
  192. {
  193. string region = uriMatch.Groups["region"].ToString();
  194. regionInfo = RegionProfileData.RequestSimProfileData(region,
  195. m_config.GridServerURL, m_config.GridSendKey, m_config.GridRecvKey);
  196. if (regionInfo == null)
  197. {
  198. m_log.InfoFormat("[LOGIN]: Got Custom Login URL {0}, can't locate region {1}", startLocationRequest, region);
  199. }
  200. else
  201. {
  202. theUser.CurrentAgent.Position = new Vector3(float.Parse(uriMatch.Groups["x"].Value),
  203. float.Parse(uriMatch.Groups["y"].Value), float.Parse(uriMatch.Groups["x"].Value));
  204. }
  205. }
  206. response.LookAt = "[r0,r1,r0]";
  207. // can be: last, home, safe, url
  208. response.StartLocation = "url";
  209. }
  210. if ((regionInfo != null) && (PrepareLoginToRegion(regionInfo, theUser, response)))
  211. {
  212. return true;
  213. }
  214. // StartLocation not available, send him to a nearby region instead
  215. //regionInfo = RegionProfileData.RequestSimProfileData("", m_config.GridServerURL, m_config.GridSendKey, m_config.GridRecvKey);
  216. //m_log.InfoFormat("[LOGIN]: StartLocation not available sending to region {0}", regionInfo.regionName);
  217. // Send him to default region instead
  218. // Load information from the gridserver
  219. ulong defaultHandle = (((ulong) m_config.DefaultX * Constants.RegionSize) << 32) |
  220. ((ulong) m_config.DefaultY * Constants.RegionSize);
  221. if ((regionInfo != null) && (defaultHandle == regionInfo.regionHandle))
  222. {
  223. m_log.ErrorFormat("[LOGIN]: Not trying the default region since this is the same as the selected region");
  224. return false;
  225. }
  226. m_log.Error("[LOGIN]: Sending user to default region " + defaultHandle + " instead");
  227. regionInfo = RegionProfileData.RequestSimProfileData(defaultHandle, m_config.GridServerURL, m_config.GridSendKey, m_config.GridRecvKey);
  228. // Customise the response
  229. //response.Home =
  230. // string.Format(
  231. // "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}",
  232. // (SimInfo.regionLocX * Constants.RegionSize),
  233. // (SimInfo.regionLocY*Constants.RegionSize),
  234. // theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z,
  235. // theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z);
  236. theUser.CurrentAgent.Position = new Vector3(128,128,0);
  237. response.StartLocation = "safe";
  238. return PrepareLoginToRegion(regionInfo, theUser, response);
  239. }
  240. /// <summary>
  241. /// Add active gestures of the user to the login response.
  242. /// </summary>
  243. /// <param name="response">
  244. /// A <see cref="LoginResponse"/>
  245. /// </param>
  246. /// <param name="theUser">
  247. /// A <see cref="UserProfileData"/>
  248. /// </param>
  249. private void AddActiveGestures(LoginResponse response, UserProfileData theUser)
  250. {
  251. List<InventoryItemBase> gestures = m_inventoryService.GetActiveGestures(theUser.ID);
  252. m_log.DebugFormat("[LOGIN]: AddActiveGestures, found {0}", gestures == null ? 0 : gestures.Count);
  253. ArrayList list = new ArrayList();
  254. if (gestures != null)
  255. {
  256. foreach (InventoryItemBase gesture in gestures)
  257. {
  258. Hashtable item = new Hashtable();
  259. item["item_id"] = gesture.ID.ToString();
  260. item["asset_id"] = gesture.AssetID.ToString();
  261. list.Add(item);
  262. }
  263. }
  264. response.ActiveGestures = list;
  265. }
  266. /// <summary>
  267. /// Prepare a login to the given region. This involves both telling the region to expect a connection
  268. /// and appropriately customising the response to the user.
  269. /// </summary>
  270. /// <param name="sim"></param>
  271. /// <param name="user"></param>
  272. /// <param name="response"></param>
  273. /// <returns>true if the region was successfully contacted, false otherwise</returns>
  274. private bool PrepareLoginToRegion(RegionProfileData regionInfo, UserProfileData user, LoginResponse response)
  275. {
  276. try
  277. {
  278. response.SimAddress = Util.GetHostFromURL(regionInfo.serverURI).ToString();
  279. response.SimPort = uint.Parse(regionInfo.serverURI.Split(new char[] { '/', ':' })[4]);
  280. response.RegionX = regionInfo.regionLocX;
  281. response.RegionY = regionInfo.regionLocY;
  282. //Not sure if the + "/CAPS/" should in fact be +"CAPS/" depending if there is already a / as part of httpServerURI
  283. string capsPath = Util.GetRandomCapsPath();
  284. response.SeedCapability = regionInfo.httpServerURI + "CAPS/" + capsPath + "0000/";
  285. // Notify the target of an incoming user
  286. m_log.InfoFormat(
  287. "[LOGIN]: Telling {0} @ {1},{2} ({3}) to prepare for client connection",
  288. regionInfo.regionName, response.RegionX, response.RegionY, regionInfo.httpServerURI);
  289. // Update agent with target sim
  290. user.CurrentAgent.Region = regionInfo.UUID;
  291. user.CurrentAgent.Handle = regionInfo.regionHandle;
  292. // Prepare notification
  293. Hashtable loginParams = new Hashtable();
  294. loginParams["session_id"] = user.CurrentAgent.SessionID.ToString();
  295. loginParams["secure_session_id"] = user.CurrentAgent.SecureSessionID.ToString();
  296. loginParams["firstname"] = user.FirstName;
  297. loginParams["lastname"] = user.SurName;
  298. loginParams["agent_id"] = user.ID.ToString();
  299. loginParams["circuit_code"] = (Int32) Convert.ToUInt32(response.CircuitCode);
  300. loginParams["startpos_x"] = user.CurrentAgent.Position.X.ToString();
  301. loginParams["startpos_y"] = user.CurrentAgent.Position.Y.ToString();
  302. loginParams["startpos_z"] = user.CurrentAgent.Position.Z.ToString();
  303. loginParams["regionhandle"] = user.CurrentAgent.Handle.ToString();
  304. loginParams["caps_path"] = capsPath;
  305. ArrayList SendParams = new ArrayList();
  306. SendParams.Add(loginParams);
  307. // Send
  308. XmlRpcRequest GridReq = new XmlRpcRequest("expect_user", SendParams);
  309. XmlRpcResponse GridResp = GridReq.Send(regionInfo.httpServerURI, 6000);
  310. if (!GridResp.IsFault)
  311. {
  312. bool responseSuccess = true;
  313. if (GridResp.Value != null)
  314. {
  315. Hashtable resp = (Hashtable) GridResp.Value;
  316. if (resp.ContainsKey("success"))
  317. {
  318. if ((string) resp["success"] == "FALSE")
  319. {
  320. responseSuccess = false;
  321. }
  322. }
  323. }
  324. if (responseSuccess)
  325. {
  326. handlerUserLoggedInAtLocation = OnUserLoggedInAtLocation;
  327. if (handlerUserLoggedInAtLocation != null)
  328. {
  329. handlerUserLoggedInAtLocation(user.ID, user.CurrentAgent.SessionID,
  330. user.CurrentAgent.Region,
  331. user.CurrentAgent.Handle,
  332. user.CurrentAgent.Position.X,
  333. user.CurrentAgent.Position.Y,
  334. user.CurrentAgent.Position.Z,
  335. user.FirstName, user.SurName);
  336. }
  337. }
  338. else
  339. {
  340. m_log.ErrorFormat("[LOGIN]: Region responded that it is not available to receive clients");
  341. return false;
  342. }
  343. }
  344. else
  345. {
  346. m_log.ErrorFormat("[LOGIN]: XmlRpc request to region failed with message {0}, code {1} ", GridResp.FaultString, GridResp.FaultCode);
  347. return false;
  348. }
  349. }
  350. catch (Exception e)
  351. {
  352. m_log.ErrorFormat("[LOGIN]: Region not available for login, {0}", e);
  353. return false;
  354. }
  355. return true;
  356. }
  357. // See LoginService
  358. protected override InventoryData GetInventorySkeleton(UUID userID)
  359. {
  360. m_log.DebugFormat(
  361. "[LOGIN]: Contacting inventory service at {0} for inventory skeleton of user {1}",
  362. m_config.InventoryUrl, userID);
  363. List<InventoryFolderBase> folders = m_inventoryService.GetInventorySkeleton(userID);
  364. if (null == folders || folders.Count == 0)
  365. {
  366. m_log.InfoFormat(
  367. "[LOGIN]: A root inventory folder for user {0} was not found. Requesting creation.", userID);
  368. // Although the create user function creates a new agent inventory along with a new user profile, some
  369. // tools are creating the user profile directly in the database without creating the inventory. At
  370. // this time we'll accomodate them by lazily creating the user inventory now if it doesn't already
  371. // exist.
  372. if (!m_inventoryService.CreateNewUserInventory(userID))
  373. {
  374. throw new Exception(
  375. String.Format(
  376. "The inventory creation request for user {0} did not succeed."
  377. + " Please contact your inventory service provider for more information.",
  378. userID));
  379. }
  380. m_log.InfoFormat("[LOGIN]: A new inventory skeleton was successfully created for user {0}", userID);
  381. folders = m_inventoryService.GetInventorySkeleton(userID);
  382. }
  383. if (folders != null && folders.Count > 0)
  384. {
  385. UUID rootID = UUID.Zero;
  386. ArrayList AgentInventoryArray = new ArrayList();
  387. Hashtable TempHash;
  388. foreach (InventoryFolderBase InvFolder in folders)
  389. {
  390. // m_log.DebugFormat("[LOGIN]: Received agent inventory folder {0}", InvFolder.name);
  391. if (InvFolder.ParentID == UUID.Zero)
  392. {
  393. rootID = InvFolder.ID;
  394. }
  395. TempHash = new Hashtable();
  396. TempHash["name"] = InvFolder.Name;
  397. TempHash["parent_id"] = InvFolder.ParentID.ToString();
  398. TempHash["version"] = (Int32) InvFolder.Version;
  399. TempHash["type_default"] = (Int32) InvFolder.Type;
  400. TempHash["folder_id"] = InvFolder.ID.ToString();
  401. AgentInventoryArray.Add(TempHash);
  402. }
  403. return new InventoryData(AgentInventoryArray, rootID);
  404. }
  405. throw new Exception(
  406. String.Format(
  407. "A root inventory folder for user {0} could not be retrieved from the inventory service",
  408. userID));
  409. }
  410. public XmlRpcResponse XmlRPCSetLoginParams(XmlRpcRequest request)
  411. {
  412. XmlRpcResponse response = new XmlRpcResponse();
  413. Hashtable requestData = (Hashtable) request.Params[0];
  414. UserProfileData userProfile;
  415. Hashtable responseData = new Hashtable();
  416. UUID uid;
  417. string pass = requestData["password"].ToString();
  418. if (!UUID.TryParse((string) requestData["avatar_uuid"], out uid))
  419. {
  420. responseData["error"] = "No authorization";
  421. response.Value = responseData;
  422. return response;
  423. }
  424. userProfile = m_userManager.GetUserProfile(uid);
  425. if (userProfile == null ||
  426. (!AuthenticateUser(userProfile, pass)) ||
  427. userProfile.GodLevel < 200)
  428. {
  429. responseData["error"] = "No authorization";
  430. response.Value = responseData;
  431. return response;
  432. }
  433. if (requestData.ContainsKey("login_level"))
  434. {
  435. m_minLoginLevel = Convert.ToInt32(requestData["login_level"]);
  436. }
  437. if (requestData.ContainsKey("login_motd"))
  438. {
  439. m_welcomeMessage = requestData["login_motd"].ToString();
  440. }
  441. response.Value = responseData;
  442. return response;
  443. }
  444. }
  445. }