From f7eac63e01d957bd9c115ac0f55d1dd6109f6aea Mon Sep 17 00:00:00 2001 From: diva Date: Sun, 29 Mar 2009 22:04:45 +0000 Subject: Another bit of refactoring to try to make sense of OpenSim.Framework.Communications. Everything that looks like a service, with service handlers, moved to .Services -- i.e. LoginService and Response, and GridInfoService. The rest of the changes were to adapt to the new locations of those files. --- .../Communications/Services/LoginService.cs | 1093 ++++++++++++++++++++ 1 file changed, 1093 insertions(+) create mode 100644 OpenSim/Framework/Communications/Services/LoginService.cs (limited to 'OpenSim/Framework/Communications/Services/LoginService.cs') diff --git a/OpenSim/Framework/Communications/Services/LoginService.cs b/OpenSim/Framework/Communications/Services/LoginService.cs new file mode 100644 index 0000000..51158c9 --- /dev/null +++ b/OpenSim/Framework/Communications/Services/LoginService.cs @@ -0,0 +1,1093 @@ +/* + * 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 OpenSim 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 System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading; +using System.Web; +using log4net; +using Nwc.XmlRpc; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Framework.Statistics; + +namespace OpenSim.Framework.Communications.Services +{ + public abstract class LoginService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_welcomeMessage = "Welcome to OpenSim"; + protected int m_minLoginLevel = 0; + protected UserManagerBase m_userManager = null; + protected Mutex m_loginMutex = new Mutex(false); + + /// + /// Used during login to send the skeleton of the OpenSim Library to the client. + /// + protected LibraryRootFolder m_libraryRootFolder; + + protected uint m_defaultHomeX; + protected uint m_defaultHomeY; + + /// + /// Used by the login service to make requests to the inventory service. + /// + protected IInterServiceInventoryServices m_inventoryService; + + /// + /// Constructor + /// + /// + /// + /// + public LoginService(UserManagerBase userManager, LibraryRootFolder libraryRootFolder, + string welcomeMess) + { + m_userManager = userManager; + m_libraryRootFolder = libraryRootFolder; + + if (welcomeMess != String.Empty) + { + m_welcomeMessage = welcomeMess; + } + } + + /// + /// If the user is already logged in, try to notify the region that the user they've got is dead. + /// + /// + public virtual void LogOffUser(UserProfileData theUser, string message) + { + } + + + /// + /// Called when we receive the client's initial XMLRPC login_to_simulator request message + /// + /// The XMLRPC request + /// The response to send + public virtual XmlRpcResponse XmlRpcLoginMethod(XmlRpcRequest request) + { + // Temporary fix + m_loginMutex.WaitOne(); + + try + { + //CFK: CustomizeResponse contains sufficient strings to alleviate the need for this. + //CKF: m_log.Info("[LOGIN]: Attempting login now..."); + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable requestData = (Hashtable)request.Params[0]; + + SniffLoginKey((Uri)request.Params[2], requestData); + + bool GoodXML = (requestData.Contains("first") && requestData.Contains("last") && + (requestData.Contains("passwd") || requestData.Contains("web_login_key"))); + + string startLocationRequest = "last"; + + UserProfileData userProfile; + LoginResponse logResponse = new LoginResponse(); + + string firstname; + string lastname; + + if (GoodXML) + { + if (requestData.Contains("start")) + { + startLocationRequest = (string)requestData["start"]; + } + + firstname = (string)requestData["first"]; + lastname = (string)requestData["last"]; + + m_log.InfoFormat( + "[LOGIN BEGIN]: XMLRPC Received login request message from user '{0}' '{1}'", + firstname, lastname); + + string clientVersion = "Unknown"; + + if (requestData.Contains("version")) + { + clientVersion = (string)requestData["version"]; + } + + m_log.DebugFormat( + "[LOGIN]: XMLRPC Client is {0}, start location is {1}", clientVersion, startLocationRequest); + + if (!TryAuthenticateXmlRpcLogin(request, firstname, lastname, out userProfile)) + { + return logResponse.CreateLoginFailedResponse(); + } + } + else + { + m_log.Info( + "[LOGIN END]: XMLRPC login_to_simulator login message did not contain all the required data"); + + return logResponse.CreateGridErrorResponse(); + } + + if (userProfile.GodLevel < m_minLoginLevel) + { + return logResponse.CreateLoginBlockedResponse(); + } + else + { + // If we already have a session... + if (userProfile.CurrentAgent != null && userProfile.CurrentAgent.AgentOnline) + { + //TODO: The following statements can cause trouble: + // If agentOnline could not turn from true back to false normally + // because of some problem, for instance, the crashment of server or client, + // the user cannot log in any longer. + userProfile.CurrentAgent.AgentOnline = false; + + m_userManager.CommitAgent(ref userProfile); + + // try to tell the region that their user is dead. + LogOffUser(userProfile, " XMLRPC You were logged off because you logged in from another location"); + + // Reject the login + + m_log.InfoFormat( + "[LOGIN END]: XMLRPC Notifying user {0} {1} that they are already logged in", + firstname, lastname); + + return logResponse.CreateAlreadyLoggedInResponse(); + } + + // Otherwise... + // Create a new agent session + + m_userManager.ResetAttachments(userProfile.ID); + + CreateAgent(userProfile, request); + + try + { + UUID agentID = userProfile.ID; + InventoryData inventData; + + try + { + inventData = GetInventorySkeleton(agentID); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[LOGIN END]: Error retrieving inventory skeleton of agent {0} - {1}", + agentID, e); + + return logResponse.CreateLoginInventoryFailedResponse(); + } + + ArrayList AgentInventoryArray = inventData.InventoryArray; + + Hashtable InventoryRootHash = new Hashtable(); + InventoryRootHash["folder_id"] = inventData.RootFolderID.ToString(); + ArrayList InventoryRoot = new ArrayList(); + InventoryRoot.Add(InventoryRootHash); + userProfile.RootInventoryFolderID = inventData.RootFolderID; + + // Inventory Library Section + Hashtable InventoryLibRootHash = new Hashtable(); + InventoryLibRootHash["folder_id"] = "00000112-000f-0000-0000-000100bba000"; + ArrayList InventoryLibRoot = new ArrayList(); + InventoryLibRoot.Add(InventoryLibRootHash); + + logResponse.InventoryLibRoot = InventoryLibRoot; + logResponse.InventoryLibraryOwner = GetLibraryOwner(); + logResponse.InventoryRoot = InventoryRoot; + logResponse.InventorySkeleton = AgentInventoryArray; + logResponse.InventoryLibrary = GetInventoryLibrary(); + + logResponse.CircuitCode = Util.RandomClass.Next(); + logResponse.Lastname = userProfile.SurName; + logResponse.Firstname = userProfile.FirstName; + logResponse.AgentID = agentID; + logResponse.SessionID = userProfile.CurrentAgent.SessionID; + logResponse.SecureSessionID = userProfile.CurrentAgent.SecureSessionID; + logResponse.Message = GetMessage(); + logResponse.BuddList = ConvertFriendListItem(m_userManager.GetUserFriendList(agentID)); + logResponse.StartLocation = startLocationRequest; + + if (CustomiseResponse(logResponse, userProfile, startLocationRequest)) + { + userProfile.LastLogin = userProfile.CurrentAgent.LoginTime; + CommitAgent(ref userProfile); + + // If we reach this point, then the login has successfully logged onto the grid + if (StatsManager.UserStats != null) + StatsManager.UserStats.AddSuccessfulLogin(); + + m_log.DebugFormat( + "[LOGIN END]: XMLRPC Authentication of user {0} {1} successful. Sending response to client.", + firstname, lastname); + + return logResponse.ToXmlRpcResponse(); + } + else + { + m_log.ErrorFormat("[LOGIN END]: XMLRPC informing user {0} {1} that login failed due to an unavailable region", firstname, lastname); + return logResponse.CreateDeadRegionResponse(); + } + } + catch (Exception e) + { + m_log.Error("[LOGIN END]: XMLRPC Login failed, " + e); + m_log.Error(e.StackTrace); + } + } + + m_log.Info("[LOGIN END]: XMLRPC Login failed. Sending back blank XMLRPC response"); + return response; + } + finally + { + m_loginMutex.ReleaseMutex(); + } + } + + protected virtual bool TryAuthenticateXmlRpcLogin(XmlRpcRequest request, string firstname, string lastname, out UserProfileData userProfile) + { + Hashtable requestData = (Hashtable)request.Params[0]; + + bool GoodLogin = false; + + userProfile = GetTheUser(firstname, lastname); + if (userProfile == null) + { + m_log.Info("[LOGIN END]: XMLRPC Could not find a profile for " + firstname + " " + lastname); + } + else + { + if (requestData.Contains("passwd")) + { + string passwd = (string)requestData["passwd"]; + GoodLogin = AuthenticateUser(userProfile, passwd); + } + if (!GoodLogin && (requestData.Contains("web_login_key"))) + { + try + { + UUID webloginkey = new UUID((string)requestData["web_login_key"]); + GoodLogin = AuthenticateUser(userProfile, webloginkey); + } + catch (Exception e) + { + m_log.InfoFormat( + "[LOGIN END]: XMLRPC Bad web_login_key: {0} for user {1} {2}, exception {3}", + requestData["web_login_key"], firstname, lastname, e); + } + } + } + + return GoodLogin; + } + + protected virtual bool TryAuthenticateLLSDLogin(string firstname, string lastname, string passwd, out UserProfileData userProfile) + { + bool GoodLogin = false; + userProfile = GetTheUser(firstname, lastname); + if (userProfile == null) + { + m_log.Info("[LOGIN]: LLSD Could not find a profile for " + firstname + " " + lastname); + + return false; + } + + GoodLogin = AuthenticateUser(userProfile, passwd); + return GoodLogin; + } + + /// + /// Called when we receive the client's initial LLSD login_to_simulator request message + /// + /// The LLSD request + /// The response to send + public OSD LLSDLoginMethod(OSD request) + { + // Temporary fix + m_loginMutex.WaitOne(); + + try + { + // bool GoodLogin = false; + + string startLocationRequest = "last"; + + UserProfileData userProfile = null; + LoginResponse logResponse = new LoginResponse(); + + if (request.Type == OSDType.Map) + { + OSDMap map = (OSDMap)request; + + if (map.ContainsKey("first") && map.ContainsKey("last") && map.ContainsKey("passwd")) + { + string firstname = map["first"].AsString(); + string lastname = map["last"].AsString(); + string passwd = map["passwd"].AsString(); + + if (map.ContainsKey("start")) + { + m_log.Info("[LOGIN]: LLSD StartLocation Requested: " + map["start"].AsString()); + startLocationRequest = map["start"].AsString(); + } + m_log.Info("[LOGIN]: LLSD Login Requested for: '" + firstname + "' '" + lastname + "' / " + passwd); + + if (!TryAuthenticateLLSDLogin(firstname, lastname, passwd, out userProfile)) + { + return logResponse.CreateLoginFailedResponseLLSD(); + } + } + else + return logResponse.CreateLoginFailedResponseLLSD(); + } + else + return logResponse.CreateLoginFailedResponseLLSD(); + + + if (userProfile.GodLevel < m_minLoginLevel) + { + return logResponse.CreateLoginBlockedResponseLLSD(); + } + else + { + // If we already have a session... + if (userProfile.CurrentAgent != null && userProfile.CurrentAgent.AgentOnline) + { + userProfile.CurrentAgent.AgentOnline = false; + + m_userManager.CommitAgent(ref userProfile); + // try to tell the region that their user is dead. + LogOffUser(userProfile, " LLSD You were logged off because you logged in from another location"); + + // Reject the login + + m_log.InfoFormat( + "[LOGIN END]: LLSD Notifying user {0} {1} that they are already logged in", + userProfile.FirstName, userProfile.SurName); + + userProfile.CurrentAgent = null; + return logResponse.CreateAlreadyLoggedInResponseLLSD(); + } + + // Otherwise... + // Create a new agent session + + m_userManager.ResetAttachments(userProfile.ID); + + CreateAgent(userProfile, request); + + try + { + UUID agentID = userProfile.ID; + + //InventoryData inventData = GetInventorySkeleton(agentID); + InventoryData inventData = null; + + try + { + inventData = GetInventorySkeleton(agentID); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[LOGIN END]: LLSD Error retrieving inventory skeleton of agent {0}, {1} - {2}", + agentID, e.GetType(), e.Message); + + return logResponse.CreateLoginFailedResponseLLSD();// .CreateLoginInventoryFailedResponseLLSD (); + } + + + ArrayList AgentInventoryArray = inventData.InventoryArray; + + Hashtable InventoryRootHash = new Hashtable(); + InventoryRootHash["folder_id"] = inventData.RootFolderID.ToString(); + ArrayList InventoryRoot = new ArrayList(); + InventoryRoot.Add(InventoryRootHash); + userProfile.RootInventoryFolderID = inventData.RootFolderID; + + + // Inventory Library Section + Hashtable InventoryLibRootHash = new Hashtable(); + InventoryLibRootHash["folder_id"] = "00000112-000f-0000-0000-000100bba000"; + ArrayList InventoryLibRoot = new ArrayList(); + InventoryLibRoot.Add(InventoryLibRootHash); + + logResponse.InventoryLibRoot = InventoryLibRoot; + logResponse.InventoryLibraryOwner = GetLibraryOwner(); + logResponse.InventoryRoot = InventoryRoot; + logResponse.InventorySkeleton = AgentInventoryArray; + logResponse.InventoryLibrary = GetInventoryLibrary(); + + logResponse.CircuitCode = (Int32)Util.RandomClass.Next(); + logResponse.Lastname = userProfile.SurName; + logResponse.Firstname = userProfile.FirstName; + logResponse.AgentID = agentID; + logResponse.SessionID = userProfile.CurrentAgent.SessionID; + logResponse.SecureSessionID = userProfile.CurrentAgent.SecureSessionID; + logResponse.Message = GetMessage(); + logResponse.BuddList = ConvertFriendListItem(m_userManager.GetUserFriendList(agentID)); + logResponse.StartLocation = startLocationRequest; + + try + { + CustomiseResponse(logResponse, userProfile, startLocationRequest); + } + catch (Exception ex) + { + m_log.Info("[LOGIN]: LLSD " + ex.ToString()); + return logResponse.CreateDeadRegionResponseLLSD(); + } + + userProfile.LastLogin = userProfile.CurrentAgent.LoginTime; + CommitAgent(ref userProfile); + + // If we reach this point, then the login has successfully logged onto the grid + if (StatsManager.UserStats != null) + StatsManager.UserStats.AddSuccessfulLogin(); + + m_log.DebugFormat( + "[LOGIN END]: LLSD Authentication of user {0} {1} successful. Sending response to client.", + userProfile.FirstName, userProfile.SurName); + + return logResponse.ToLLSDResponse(); + } + catch (Exception ex) + { + m_log.Info("[LOGIN]: LLSD " + ex.ToString()); + return logResponse.CreateFailedResponseLLSD(); + } + } + } + finally + { + m_loginMutex.ReleaseMutex(); + } + } + + public Hashtable ProcessHTMLLogin(Hashtable keysvals) + { + // Matches all unspecified characters + // Currently specified,; lowercase letters, upper case letters, numbers, underline + // period, space, parens, and dash. + + Regex wfcut = new Regex("[^a-zA-Z0-9_\\.\\$ \\(\\)\\-]"); + + Hashtable returnactions = new Hashtable(); + int statuscode = 200; + + string firstname = String.Empty; + string lastname = String.Empty; + string location = String.Empty; + string region = String.Empty; + string grid = String.Empty; + string channel = String.Empty; + string version = String.Empty; + string lang = String.Empty; + string password = String.Empty; + string errormessages = String.Empty; + + // the client requires the HTML form field be named 'username' + // however, the data it sends when it loads the first time is 'firstname' + // another one of those little nuances. + + if (keysvals.Contains("firstname")) + firstname = wfcut.Replace((string)keysvals["firstname"], String.Empty, 99999); + + if (keysvals.Contains("username")) + firstname = wfcut.Replace((string)keysvals["username"], String.Empty, 99999); + + if (keysvals.Contains("lastname")) + lastname = wfcut.Replace((string)keysvals["lastname"], String.Empty, 99999); + + if (keysvals.Contains("location")) + location = wfcut.Replace((string)keysvals["location"], String.Empty, 99999); + + if (keysvals.Contains("region")) + region = wfcut.Replace((string)keysvals["region"], String.Empty, 99999); + + if (keysvals.Contains("grid")) + grid = wfcut.Replace((string)keysvals["grid"], String.Empty, 99999); + + if (keysvals.Contains("channel")) + channel = wfcut.Replace((string)keysvals["channel"], String.Empty, 99999); + + if (keysvals.Contains("version")) + version = wfcut.Replace((string)keysvals["version"], String.Empty, 99999); + + if (keysvals.Contains("lang")) + lang = wfcut.Replace((string)keysvals["lang"], String.Empty, 99999); + + if (keysvals.Contains("password")) + password = wfcut.Replace((string)keysvals["password"], String.Empty, 99999); + + // load our login form. + string loginform = GetLoginForm(firstname, lastname, location, region, grid, channel, version, lang, password, errormessages); + + if (keysvals.ContainsKey("show_login_form")) + { + UserProfileData user = GetTheUser(firstname, lastname); + bool goodweblogin = false; + + if (user != null) + goodweblogin = AuthenticateUser(user, password); + + if (goodweblogin) + { + UUID webloginkey = UUID.Random(); + m_userManager.StoreWebLoginKey(user.ID, webloginkey); + //statuscode = 301; + + // string redirectURL = "about:blank?redirect-http-hack=" + + // HttpUtility.UrlEncode("secondlife:///app/login?first_name=" + firstname + "&last_name=" + + // lastname + + // "&location=" + location + "&grid=Other&web_login_key=" + webloginkey.ToString()); + //m_log.Info("[WEB]: R:" + redirectURL); + returnactions["int_response_code"] = statuscode; + //returnactions["str_redirect_location"] = redirectURL; + //returnactions["str_response_string"] = "GoodLogin"; + returnactions["str_response_string"] = webloginkey.ToString(); + } + else + { + errormessages = "The Username and password supplied did not match our records. Check your caps lock and try again"; + + loginform = GetLoginForm(firstname, lastname, location, region, grid, channel, version, lang, password, errormessages); + returnactions["int_response_code"] = statuscode; + returnactions["str_response_string"] = loginform; + } + } + else + { + returnactions["int_response_code"] = statuscode; + returnactions["str_response_string"] = loginform; + } + return returnactions; + } + + public string GetLoginForm(string firstname, string lastname, string location, string region, + string grid, string channel, string version, string lang, + string password, string errormessages) + { + // inject our values in the form at the markers + + string loginform = String.Empty; + string file = Path.Combine(Util.configDir(), "http_loginform.html"); + if (!File.Exists(file)) + { + loginform = GetDefaultLoginForm(); + } + else + { + StreamReader sr = File.OpenText(file); + loginform = sr.ReadToEnd(); + sr.Close(); + } + + loginform = loginform.Replace("[$firstname]", firstname); + loginform = loginform.Replace("[$lastname]", lastname); + loginform = loginform.Replace("[$location]", location); + loginform = loginform.Replace("[$region]", region); + loginform = loginform.Replace("[$grid]", grid); + loginform = loginform.Replace("[$channel]", channel); + loginform = loginform.Replace("[$version]", version); + loginform = loginform.Replace("[$lang]", lang); + loginform = loginform.Replace("[$password]", password); + loginform = loginform.Replace("[$errors]", errormessages); + + return loginform; + } + + public string GetDefaultLoginForm() + { + string responseString = + ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += "OpenSim Login"; + responseString += "
"; + responseString += "
"; + + responseString += "
"; + + responseString += "
[$errors]
"; + responseString += "
"; + responseString += "First Name:"; + responseString += ""; + responseString += "
"; + responseString += "
"; + responseString += "Last Name:"; + responseString += ""; + responseString += "
"; + responseString += "
"; + responseString += "Password:"; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += "
"; + responseString += "
"; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += ""; + responseString += "
"; + responseString += ""; + responseString += "
"; + responseString += "
Connecting...
"; + + responseString += "
"; + + responseString += "
[$channel] | [$version]=[$lang]
"; + responseString += "
"; + responseString += ""; + responseString += "
"; + responseString += ""; + responseString += ""; + responseString += ""; + + return responseString; + } + + /// + /// Saves a target agent to the database + /// + /// The users profile + /// Successful? + public bool CommitAgent(ref UserProfileData profile) + { + return m_userManager.CommitAgent(ref profile); + } + + /// + /// Checks a user against it's password hash + /// + /// The users profile + /// The supplied password + /// Authenticated? + public virtual bool AuthenticateUser(UserProfileData profile, string password) + { + bool passwordSuccess = false; + //m_log.InfoFormat("[LOGIN]: Authenticating {0} {1} ({2})", profile.FirstName, profile.SurName, profile.ID); + + // Web Login method seems to also occasionally send the hashed password itself + + // we do this to get our hash in a form that the server password code can consume + // when the web-login-form submits the password in the clear (supposed to be over SSL!) + if (!password.StartsWith("$1$")) + password = "$1$" + Util.Md5Hash(password); + + password = password.Remove(0, 3); //remove $1$ + + string s = Util.Md5Hash(password + ":" + profile.PasswordSalt); + // Testing... + //m_log.Info("[LOGIN]: SubHash:" + s + " userprofile:" + profile.passwordHash); + //m_log.Info("[LOGIN]: userprofile:" + profile.passwordHash + " SubCT:" + password); + + passwordSuccess = (profile.PasswordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase) + || profile.PasswordHash.Equals(password, StringComparison.InvariantCultureIgnoreCase)); + + return passwordSuccess; + } + + public virtual bool AuthenticateUser(UserProfileData profile, UUID webloginkey) + { + bool passwordSuccess = false; + m_log.InfoFormat("[LOGIN]: Authenticating {0} {1} ({2})", profile.FirstName, profile.SurName, profile.ID); + + // Match web login key unless it's the default weblogin key UUID.Zero + passwordSuccess = ((profile.WebLoginKey == webloginkey) && profile.WebLoginKey != UUID.Zero); + + return passwordSuccess; + } + + /// + /// + /// + /// + /// + public void CreateAgent(UserProfileData profile, XmlRpcRequest request) + { + m_userManager.CreateAgent(profile, request); + } + + public void CreateAgent(UserProfileData profile, OSD request) + { + m_userManager.CreateAgent(profile, request); + } + + /// + /// + /// + /// + /// + /// + public virtual UserProfileData GetTheUser(string firstname, string lastname) + { + return m_userManager.GetUserProfile(firstname, lastname); + } + + /// + /// + /// + /// + public virtual string GetMessage() + { + return m_welcomeMessage; + } + + private static LoginResponse.BuddyList ConvertFriendListItem(List LFL) + { + LoginResponse.BuddyList buddylistreturn = new LoginResponse.BuddyList(); + foreach (FriendListItem fl in LFL) + { + LoginResponse.BuddyList.BuddyInfo buddyitem = new LoginResponse.BuddyList.BuddyInfo(fl.Friend); + buddyitem.BuddyID = fl.Friend; + buddyitem.BuddyRightsHave = (int)fl.FriendListOwnerPerms; + buddyitem.BuddyRightsGiven = (int)fl.FriendPerms; + buddylistreturn.AddNewBuddy(buddyitem); + } + return buddylistreturn; + } + + /// + /// Converts the inventory library skeleton into the form required by the rpc request. + /// + /// + protected virtual ArrayList GetInventoryLibrary() + { + Dictionary rootFolders + = m_libraryRootFolder.RequestSelfAndDescendentFolders(); + ArrayList folderHashes = new ArrayList(); + + foreach (InventoryFolderBase folder in rootFolders.Values) + { + Hashtable TempHash = new Hashtable(); + TempHash["name"] = folder.Name; + TempHash["parent_id"] = folder.ParentID.ToString(); + TempHash["version"] = (Int32)folder.Version; + TempHash["type_default"] = (Int32)folder.Type; + TempHash["folder_id"] = folder.ID.ToString(); + folderHashes.Add(TempHash); + } + + return folderHashes; + } + + /// + /// + /// + /// + protected virtual ArrayList GetLibraryOwner() + { + //for now create random inventory library owner + Hashtable TempHash = new Hashtable(); + TempHash["agent_id"] = "11111111-1111-0000-0000-000100bba000"; + ArrayList inventoryLibOwner = new ArrayList(); + inventoryLibOwner.Add(TempHash); + return inventoryLibOwner; + } + + public class InventoryData + { + public ArrayList InventoryArray = null; + public UUID RootFolderID = UUID.Zero; + + public InventoryData(ArrayList invList, UUID rootID) + { + InventoryArray = invList; + RootFolderID = rootID; + } + } + + protected void SniffLoginKey(Uri uri, Hashtable requestData) + { + string uri_str = uri.ToString(); + string[] parts = uri_str.Split(new char[] { '=' }); + if (parts.Length > 1) + { + string web_login_key = parts[1]; + requestData.Add("web_login_key", web_login_key); + m_log.InfoFormat("[LOGIN]: Login with web_login_key {0}", web_login_key); + } + } + + /// + /// Customises the login response and fills in missing values. This method also tells the login region to + /// expect a client connection. + /// + /// The existing response + /// The user profile + /// The requested start location + /// true on success, false if the region was not successfully told to expect a user connection + public bool CustomiseResponse(LoginResponse response, UserProfileData theUser, string startLocationRequest) + { + // add active gestures to login-response + AddActiveGestures(response, theUser); + + // HomeLocation + RegionInfo homeInfo = null; + + // use the homeRegionID if it is stored already. If not, use the regionHandle as before + UUID homeRegionId = theUser.HomeRegionID; + ulong homeRegionHandle = theUser.HomeRegion; + if (homeRegionId != UUID.Zero) + { + homeInfo = GetRegionInfo(homeRegionId); + } + else + { + homeInfo = GetRegionInfo(homeRegionHandle); + } + + if (homeInfo != null) + { + response.Home = + string.Format( + "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}", + (homeInfo.RegionLocX * Constants.RegionSize), + (homeInfo.RegionLocY * Constants.RegionSize), + theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z, + theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z); + } + else + { + m_log.InfoFormat("not found the region at {0} {1}", theUser.HomeRegionX, theUser.HomeRegionY); + // Emergency mode: Home-region isn't available, so we can't request the region info. + // Use the stored home regionHandle instead. + // NOTE: If the home-region moves, this will be wrong until the users update their user-profile again + ulong regionX = homeRegionHandle >> 32; + ulong regionY = homeRegionHandle & 0xffffffff; + response.Home = + string.Format( + "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}", + regionX, regionY, + theUser.HomeLocation.X, theUser.HomeLocation.Y, theUser.HomeLocation.Z, + theUser.HomeLookAt.X, theUser.HomeLookAt.Y, theUser.HomeLookAt.Z); + + m_log.InfoFormat("[LOGIN] Home region of user {0} {1} is not available; using computed region position {2} {3}", + theUser.FirstName, theUser.SurName, + regionX, regionY); + } + + // StartLocation + RegionInfo regionInfo = null; + if (startLocationRequest == "home") + { + regionInfo = homeInfo; + theUser.CurrentAgent.Position = theUser.HomeLocation; + response.LookAt = "[r" + theUser.HomeLookAt.X.ToString() + ",r" + theUser.HomeLookAt.Y.ToString() + ",r" + theUser.HomeLookAt.Z.ToString() + "]"; + } + else if (startLocationRequest == "last") + { + UUID lastRegion = theUser.CurrentAgent.Region; + regionInfo = GetRegionInfo(lastRegion); + response.LookAt = "[r" + theUser.CurrentAgent.LookAt.X.ToString() + ",r" + theUser.CurrentAgent.LookAt.Y.ToString() + ",r" + theUser.CurrentAgent.LookAt.Z.ToString() + "]"; + } + else + { + Regex reURI = new Regex(@"^uri:(?[^&]+)&(?\d+)&(?\d+)&(?\d+)$"); + Match uriMatch = reURI.Match(startLocationRequest); + if (uriMatch == null) + { + m_log.InfoFormat("[LOGIN]: Got Custom Login URL {0}, but can't process it", startLocationRequest); + } + else + { + string region = uriMatch.Groups["region"].ToString(); + regionInfo = RequestClosestRegion(region); + if (regionInfo == null) + { + m_log.InfoFormat("[LOGIN]: Got Custom Login URL {0}, can't locate region {1}", startLocationRequest, region); + } + else + { + theUser.CurrentAgent.Position = new Vector3(float.Parse(uriMatch.Groups["x"].Value), + float.Parse(uriMatch.Groups["y"].Value), float.Parse(uriMatch.Groups["z"].Value)); + } + } + response.LookAt = "[r0,r1,r0]"; + // can be: last, home, safe, url + response.StartLocation = "url"; + } + + if ((regionInfo != null) && (PrepareLoginToRegion(regionInfo, theUser, response))) + { + return true; + } + + // StartLocation not available, send him to a nearby region instead + // regionInfo = m_gridService.RequestClosestRegion(""); + //m_log.InfoFormat("[LOGIN]: StartLocation not available sending to region {0}", regionInfo.regionName); + + // Send him to default region instead + ulong defaultHandle = (((ulong)m_defaultHomeX * Constants.RegionSize) << 32) | + ((ulong)m_defaultHomeY * Constants.RegionSize); + + if ((regionInfo != null) && (defaultHandle == regionInfo.RegionHandle)) + { + m_log.ErrorFormat("[LOGIN]: Not trying the default region since this is the same as the selected region"); + return false; + } + + m_log.Error("[LOGIN]: Sending user to default region " + defaultHandle + " instead"); + regionInfo = GetRegionInfo(defaultHandle); + + if (regionInfo == null) + { + m_log.ErrorFormat("[LOGIN]: No default region available. Aborting."); + return false; + } + + theUser.CurrentAgent.Position = new Vector3(128, 128, 0); + response.StartLocation = "safe"; + + return PrepareLoginToRegion(regionInfo, theUser, response); + } + + protected abstract RegionInfo RequestClosestRegion(string region); + protected abstract RegionInfo GetRegionInfo(ulong homeRegionHandle); + protected abstract RegionInfo GetRegionInfo(UUID homeRegionId); + protected abstract bool PrepareLoginToRegion(RegionInfo regionInfo, UserProfileData user, LoginResponse response); + + /// + /// Add active gestures of the user to the login response. + /// + /// + /// A + /// + /// + /// A + /// + protected void AddActiveGestures(LoginResponse response, UserProfileData theUser) + { + List gestures = m_inventoryService.GetActiveGestures(theUser.ID); + //m_log.DebugFormat("[LOGIN]: AddActiveGestures, found {0}", gestures == null ? 0 : gestures.Count); + ArrayList list = new ArrayList(); + if (gestures != null) + { + foreach (InventoryItemBase gesture in gestures) + { + Hashtable item = new Hashtable(); + item["item_id"] = gesture.ID.ToString(); + item["asset_id"] = gesture.AssetID.ToString(); + list.Add(item); + } + } + response.ActiveGestures = list; + } + + /// + /// Get the initial login inventory skeleton (in other words, the folder structure) for the given user. + /// + /// + /// + /// This will be thrown if there is a problem with the inventory service + protected InventoryData GetInventorySkeleton(UUID userID) + { + List folders = m_inventoryService.GetInventorySkeleton(userID); + + // If we have user auth but no inventory folders for some reason, create a new set of folders. + if (folders == null || folders.Count == 0) + { + m_log.InfoFormat( + "[LOGIN]: A root inventory folder for user {0} was not found. Requesting creation.", userID); + + // Although the create user function creates a new agent inventory along with a new user profile, some + // tools are creating the user profile directly in the database without creating the inventory. At + // this time we'll accomodate them by lazily creating the user inventory now if it doesn't already + // exist. + if (!m_inventoryService.CreateNewUserInventory(userID)) + { + throw new Exception( + String.Format( + "The inventory creation request for user {0} did not succeed." + + " Please contact your inventory service provider for more information.", + userID)); + } + + m_log.InfoFormat("[LOGIN]: A new inventory skeleton was successfully created for user {0}", userID); + + folders = m_inventoryService.GetInventorySkeleton(userID); + + if (folders == null || folders.Count == 0) + { + throw new Exception( + String.Format( + "A root inventory folder for user {0} could not be retrieved from the inventory service", + userID)); + } + } + + UUID rootID = UUID.Zero; + ArrayList AgentInventoryArray = new ArrayList(); + Hashtable TempHash; + foreach (InventoryFolderBase InvFolder in folders) + { + if (InvFolder.ParentID == UUID.Zero) + { + rootID = InvFolder.ID; + } + TempHash = new Hashtable(); + TempHash["name"] = InvFolder.Name; + TempHash["parent_id"] = InvFolder.ParentID.ToString(); + TempHash["version"] = (Int32)InvFolder.Version; + TempHash["type_default"] = (Int32)InvFolder.Type; + TempHash["folder_id"] = InvFolder.ID.ToString(); + AgentInventoryArray.Add(TempHash); + } + + return new InventoryData(AgentInventoryArray, rootID); + } + } +} -- cgit v1.1