diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index d89bb3a3aa..728cda0d4a 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -35,7 +35,7 @@ using System.IO; using System.IO.Compression; using System.Net; using System.Net.Sockets; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; @@ -378,20 +378,20 @@ namespace OpenSim.Framework } return sb.ToString(); - } - - /// - /// Is the platform Windows? - /// - /// true if so, false otherwise - public static bool IsWindows() - { - PlatformID platformId = Environment.OSVersion.Platform; - - return (platformId == PlatformID.Win32NT - || platformId == PlatformID.Win32S - || platformId == PlatformID.Win32Windows - || platformId == PlatformID.WinCE); + } + + /// + /// Is the platform Windows? + /// + /// true if so, false otherwise + public static bool IsWindows() + { + PlatformID platformId = Environment.OSVersion.Platform; + + return (platformId == PlatformID.Win32NT + || platformId == PlatformID.Win32S + || platformId == PlatformID.Win32Windows + || platformId == PlatformID.WinCE); } public static bool LoadArchSpecificWindowsDll(string libraryName) @@ -1516,27 +1516,27 @@ namespace OpenSim.Framework } return data; - } - - /// - /// Used to trigger an early library load on Windows systems. - /// - /// - /// Required to get 32-bit and 64-bit processes to automatically use the - /// appropriate native library. - /// - /// - /// - [DllImport("kernel32.dll")] - public static extern IntPtr LoadLibrary(string dllToLoad); - - /// - /// Determine whether the current process is 64 bit - /// - /// true if so, false if not - public static bool Is64BitProcess() - { - return IntPtr.Size == 8; + } + + /// + /// Used to trigger an early library load on Windows systems. + /// + /// + /// Required to get 32-bit and 64-bit processes to automatically use the + /// appropriate native library. + /// + /// + /// + [DllImport("kernel32.dll")] + public static extern IntPtr LoadLibrary(string dllToLoad); + + /// + /// Determine whether the current process is 64 bit + /// + /// true if so, false if not + public static bool Is64BitProcess() + { + return IntPtr.Size == 8; } #region FireAndForget Threading Pattern @@ -1952,11 +1952,12 @@ namespace OpenSim.Framework #region Universal User Identifiers /// /// - /// uuid[;endpoint[;name]] - /// - /// - /// - /// + /// uuid[;endpoint[;first last[;secret]]] + /// the uuid part + /// the endpoint part (e.g. http://foo.com) + /// the first name part (e.g. Test) + /// the last name part (e.g User) + /// the secret part public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) { uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; secret = string.Empty; @@ -1985,31 +1986,64 @@ namespace OpenSim.Framework } /// - /// + /// Produces a universal (HG) system-facing identifier given the information /// /// - /// uuid[;endpoint[;name]] + /// uuid[;homeURI[;first last]] public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit) { if (acircuit.ServiceURLs.ContainsKey("HomeURI")) - { - string agentsURI = acircuit.ServiceURLs["HomeURI"].ToString(); - if (!agentsURI.EndsWith("/")) - agentsURI += "/"; - - // This is ugly, but there's no other way, given that the name is changed - // in the agent circuit data for foreigners - if (acircuit.lastname.Contains("@")) - { - string[] parts = acircuit.firstname.Split(new char[] { '.' }); - if (parts.Length == 2) - return acircuit.AgentID.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1]; - } - return acircuit.AgentID.ToString() + ";" + agentsURI + ";" + acircuit.firstname + " " + acircuit.lastname; - } + return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString()); else return acircuit.AgentID.ToString(); - } + } + + /// + /// Produces a universal (HG) system-facing identifier given the information + /// + /// UUID of the user + /// first name (e.g Test) + /// last name (e.g. User) + /// homeURI (e.g. http://foo.com) + /// a string of the form uuid[;homeURI[;first last]] + public static string UniversalIdentifier(UUID id, String firstName, String lastName, String homeURI) + { + string agentsURI = homeURI; + if (!agentsURI.EndsWith("/")) + agentsURI += "/"; + + // This is ugly, but there's no other way, given that the name is changed + // in the agent circuit data for foreigners + if (lastName.Contains("@")) + { + string[] parts = firstName.Split(new char[] { '.' }); + if (parts.Length == 2) + return id.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1]; + } + return id.ToString() + ";" + agentsURI + ";" + firstName + " " + lastName; + + } + + /// + /// Produces a universal (HG) user-facing name given the information + /// + /// + /// + /// + /// string of the form first.last @foo.com or first last + public static string UniversalName(String firstName, String lastName, String homeURI) + { + Uri uri = null; + try + { + uri = new Uri(homeURI); + } + catch (UriFormatException) + { + return firstName + " " + lastName; + } + return firstName + "." + lastName + " " + "@" + uri.Authority; + } #endregion } } diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index 5d94ff7e1c..ca8d8e64fc 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -550,7 +550,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends UUID principalID = new UUID(im.fromAgentID); UUID friendID = new UUID(im.toAgentID); - m_log.DebugFormat("[FRIENDS]: {0} ({1}) offered friendship to {2}", principalID, im.fromAgentName, friendID); + m_log.DebugFormat("[FRIENDS]: {0} ({1}) offered friendship to {2} ({3})", principalID, client.FirstName + client.LastName, friendID, im.fromAgentName); + + // Check that the friendship doesn't exist yet + FriendInfo[] finfos = GetFriends(principalID); + if (finfos != null) + { + FriendInfo f = GetFriend(finfos, friendID); + if (f != null) + { + client.SendAgentAlertMessage("This person is already your friend. Please delete it first if you want to reestablish the friendship.", false); + return; + } + } // This user wants to be friends with the other user. // Let's add the relation backwards, in case the other is not online @@ -561,7 +573,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends } } - private void ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im) + protected virtual bool ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im) { // !!!!!!!! This is a hack so that we don't have to keep state (transactionID/imSessionID) // We stick this agent's ID as imSession, so that it's directly available on the receiving end @@ -570,7 +582,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // Try the local sim if (LocalFriendshipOffered(friendID, im)) - return; + { + m_log.DebugFormat("[XXX]: LocalFriendshipOffered successes"); + return true; + } // The prospective friend is not here [as root]. Let's forward. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); @@ -581,9 +596,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends { GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message); + return true; } } // If the prospective friend is not online, he'll get the message upon login. + return false; } protected virtual string GetFriendshipRequesterName(UUID agentID) @@ -592,7 +609,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName; } - private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List callingCardFolders) + protected virtual void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List callingCardFolders) { m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", client.AgentId, friendID); @@ -762,7 +779,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends #region Local - public bool LocalFriendshipOffered(UUID toID, GridInstantMessage im) + public virtual bool LocalFriendshipOffered(UUID toID, GridInstantMessage im) { IClientAPI friendClient = LocateClientObject(toID); if (friendClient != null) @@ -925,7 +942,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return FriendsService.GetFriends(client.AgentId); } - private void RecacheFriends(IClientAPI client) + protected void RecacheFriends(IClientAPI client) { // FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event // is on the critical path for transferring an avatar from one region to another. diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs index 9c53fc4ac8..0fe1134d57 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs @@ -61,6 +61,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends } } + protected HGFriendsServicesConnector m_HGFriendsConnector = new HGFriendsServicesConnector(); + #region ISharedRegionModule public override string Name { @@ -94,6 +96,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends #endregion + protected override void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List callingCardFolders) + { + // Update the local cache. Yes, we need to do it right here + // because the HGFriendsService placed something on the DB + // from under the sim + base.OnApproveFriendRequest(client, agentID, friendID, callingCardFolders); + } + protected override bool CacheFriends(IClientAPI client) { // m_log.DebugFormat("[HGFRIENDS MODULE]: Entered CacheFriends for {0}", client.Name); @@ -183,91 +193,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetOnlineFriends for {0}", userID); } - //protected override void GetOnlineFriends(UUID userID, List friendList, /*collector*/ List online) - //{ - // // Let's single out the UUIs - // List localFriends = new List(); - // List foreignFriends = new List(); - // string tmp = string.Empty; - - // foreach (string s in friendList) - // { - // UUID id; - // if (UUID.TryParse(s, out id)) - // localFriends.Add(s); - // else if (Util.ParseUniversalUserIdentifier(s, out id, out tmp, out tmp, out tmp, out tmp)) - // { - // foreignFriends.Add(s); - // // add it here too, who knows maybe the foreign friends happens to be on this grid - // localFriends.Add(id.ToString()); - // } - // } - - // // OK, see who's present on this grid - // List toBeRemoved = new List(); - // PresenceInfo[] presence = PresenceService.GetAgents(localFriends.ToArray()); - // foreach (PresenceInfo pi in presence) - // { - // UUID presenceID; - // if (UUID.TryParse(pi.UserID, out presenceID)) - // { - // online.Add(presenceID); - // foreach (string s in foreignFriends) - // if (s.StartsWith(pi.UserID)) - // toBeRemoved.Add(s); - // } - // } - - // foreach (string s in toBeRemoved) - // foreignFriends.Remove(s); - - // // OK, let's send this up the stack, and leave a closure here - // // collecting online friends in other grids - // Util.FireAndForget(delegate { CollectOnlineFriendsElsewhere(userID, foreignFriends); }); - - //} - - //private void CollectOnlineFriendsElsewhere(UUID userID, List foreignFriends) - //{ - // // let's divide the friends on a per-domain basis - // Dictionary> friendsPerDomain = new Dictionary>(); - // foreach (string friend in foreignFriends) - // { - // UUID friendID; - // if (!UUID.TryParse(friend, out friendID)) - // { - // // it's a foreign friend - // string url = string.Empty, tmp = string.Empty; - // if (Util.ParseUniversalUserIdentifier(friend, out friendID, out url, out tmp, out tmp, out tmp)) - // { - // if (!friendsPerDomain.ContainsKey(url)) - // friendsPerDomain[url] = new List(); - // friendsPerDomain[url].Add(friend); - // } - // } - // } - - // // Now, call those worlds - - // foreach (KeyValuePair> kvp in friendsPerDomain) - // { - // List ids = new List(); - // foreach (string f in kvp.Value) - // ids.Add(f); - // UserAgentServiceConnector uConn = new UserAgentServiceConnector(kvp.Key); - // List online = uConn.GetOnlineFriends(userID, ids); - // // Finally send the notifications to the user - // // this whole process may take a while, so let's check at every - // // iteration that the user is still here - // IClientAPI client = LocateClientObject(userID); - // if (client != null) - // client.SendAgentOnline(online.ToArray()); - // else - // break; - // } - - //} - protected override void StatusNotify(List friendList, UUID userID, bool online) { // m_log.DebugFormat("[HGFRIENDS MODULE]: Entering StatusNotify for {0}", userID); @@ -335,12 +260,25 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return true; // fid is not a UUID... - string url = string.Empty, tmp = string.Empty; - if (Util.ParseUniversalUserIdentifier(fid, out agentID, out url, out first, out last, out tmp)) + string url = string.Empty, tmp = string.Empty, f = string.Empty, l = string.Empty; + m_log.DebugFormat("[YYY]: FID {0}", fid); + if (Util.ParseUniversalUserIdentifier(fid, out agentID, out url, out f, out l, out tmp)) { - IUserManagement userMan = m_Scenes[0].RequestModuleInterface(); - userMan.AddUser(agentID, first, last, url); + m_log.DebugFormat("[YYY]: Adding user {0} {1} {2}", f, l, url); + m_uMan.AddUser(agentID, f, l, url); + string name = m_uMan.GetUserName(agentID); + string[] parts = name.Trim().Split(new char[] {' '}); + if (parts.Length == 2) + { + first = parts[0]; + last = parts[1]; + } + else + { + first = f; + last = l; + } return true; } return false; @@ -348,13 +286,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends protected override string GetFriendshipRequesterName(UUID agentID) { - // For the time being we assume that HG friendship requests can only happen - // when avies are on the same region. - IClientAPI client = LocateClientObject(agentID); - if (client != null) - return client.FirstName + " " + client.LastName; - else - return base.GetFriendshipRequesterName(agentID); + return m_uMan.GetUserName(agentID); } protected override string FriendshipMessage(string friendID) @@ -392,10 +324,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends AgentCircuitData agentClientCircuit = ((Scene)(client.Scene)).AuthenticateHandler.GetAgentCircuitData(client.CircuitCode); if (agentClientCircuit != null) { - string agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit); + //[XXX] string agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit); - finfos = FriendsService.GetFriends(agentUUI); - m_log.DebugFormat("[HGFRIENDS MODULE]: Fetched {0} local friends for visitor {1}", finfos.Length, agentUUI); + finfos = FriendsService.GetFriends(client.AgentId.ToString()); + m_log.DebugFormat("[HGFRIENDS MODULE]: Fetched {0} local friends for visitor {1}", finfos.Length, client.AgentId.ToString()); } // m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetFriendsFromService for {0}", client.Name); @@ -454,16 +386,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends friendIsLocal = UserManagementModule.IsLocalGridUser(friendID); } - // Are they both local users? - if (agentIsLocal && friendIsLocal) + // Is the requester a local user? + if (agentIsLocal) { // local grid users - m_log.DebugFormat("[HGFRIENDS MODULE]: Users are both local"); + m_log.DebugFormat("[HGFRIENDS MODULE]: Friendship requester is local. Storing backwards."); + base.StoreBackwards(friendID, agentID); return; } - // no provision for this temporary friendship state + // no provision for this temporary friendship state when user is not local //FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0); } @@ -501,12 +434,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends agentClientCircuit = ((Scene)(agentClient.Scene)).AuthenticateHandler.GetAgentCircuitData(agentClient.CircuitCode); agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit); agentFriendService = agentClientCircuit.ServiceURLs["FriendsServerURI"].ToString(); + RecacheFriends(agentClient); } if (friendClient != null) { friendClientCircuit = ((Scene)(friendClient.Scene)).AuthenticateHandler.GetAgentCircuitData(friendClient.CircuitCode); friendUUI = Util.ProduceUserUniversalIdentifier(friendClientCircuit); friendFriendService = friendClientCircuit.ServiceURLs["FriendsServerURI"].ToString(); + RecacheFriends(friendClient); } m_log.DebugFormat("[HGFRIENDS MODULE] HG Friendship! thisUUI={0}; friendUUI={1}; foreignThisFriendService={2}; foreignFriendFriendService={3}", @@ -515,14 +450,18 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // Generate a random 8-character hex number that will sign this friendship string secret = UUID.Random().ToString().Substring(0, 8); + string theFriendUUID = friendUUI + ";" + secret; + string agentUUID = agentUUI + ";" + secret; + if (agentIsLocal) // agent is local, 'friend' is foreigner { // This may happen when the agent returned home, in which case the friend is not there // We need to look for its information in the friends list itself + FriendInfo[] finfos = null; bool confirming = false; if (friendUUI == string.Empty) { - FriendInfo[] finfos = GetFriends(agentID); + finfos = GetFriends(agentID); foreach (FriendInfo finfo in finfos) { if (finfo.TheirFlags == -1) @@ -530,29 +469,57 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends if (finfo.Friend.StartsWith(friendID.ToString())) { friendUUI = finfo.Friend; + theFriendUUID = friendUUI; + UUID utmp = UUID.Zero; String url = String.Empty; String first = String.Empty, last = String.Empty, tmp = String.Empty; + // If it's confirming the friendship, we already have the full UUI with the secret + if (Util.ParseUniversalUserIdentifier(theFriendUUID, out utmp, out url, out first, out last, out secret)) + { + agentUUID = agentUUI + ";" + secret; + m_uMan.AddUser(utmp, first, last, url); + } confirming = true; + break; } } } - } + if (!confirming) + { + friendUUI = m_uMan.GetUserUUI(friendID); + theFriendUUID = friendUUI + ";" + secret; + } - // If it's confirming the friendship, we already have the full friendUUI with the secret - string theFriendUUID = confirming ? friendUUI : friendUUI + ";" + secret; + friendFriendService = m_uMan.GetUserServerURL(friendID, "FriendsServerURI"); + + // m_log.DebugFormat("[HGFRIENDS MODULE] HG Friendship! thisUUI={0}; friendUUI={1}; foreignThisFriendService={2}; foreignFriendFriendService={3}", + // agentUUI, friendUUI, agentFriendService, friendFriendService); + + } + + // Delete any previous friendship relations + DeletePreviousRelations(agentID, friendID); // store in the local friends service a reference to the foreign friend FriendsService.StoreFriend(agentID.ToString(), theFriendUUID, 1); // and also the converse FriendsService.StoreFriend(theFriendUUID, agentID.ToString(), 1); - if (!confirming && friendClientCircuit != null) - { + //if (!confirming) + //{ // store in the foreign friends service a reference to the local agent - HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(friendFriendService, friendClientCircuit.SessionID, friendClientCircuit.ServiceSessionID); - friendsConn.NewFriendship(friendID, agentUUI + ";" + secret); - } + HGFriendsServicesConnector friendsConn = null; + if (friendClientCircuit != null) // the friend is here, validate session + friendsConn = new HGFriendsServicesConnector(friendFriendService, friendClientCircuit.SessionID, friendClientCircuit.ServiceSessionID); + else // the friend is not here, he initiated the request in his home world + friendsConn = new HGFriendsServicesConnector(friendFriendService); + + friendsConn.NewFriendship(friendID, agentUUID); + //} } else if (friendIsLocal) // 'friend' is local, agent is foreigner { + // Delete any previous friendship relations + DeletePreviousRelations(agentID, friendID); + // store in the local friends service a reference to the foreign agent FriendsService.StoreFriend(friendID.ToString(), agentUUI + ";" + secret, 1); // and also the converse @@ -582,6 +549,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // my brain hurts now } + private void DeletePreviousRelations(UUID a1, UUID a2) + { + // Delete any previous friendship relations + FriendInfo[] finfos = null; + FriendInfo f = null; + finfos = GetFriends(a1); + if (finfos != null) + { + f = GetFriend(finfos, a2); + if (f != null) + { + FriendsService.Delete(a1, f.Friend); + // and also the converse + FriendsService.Delete(f.Friend, a1.ToString()); + } + } + + finfos = GetFriends(a2); + if (finfos != null) + { + f = GetFriend(finfos, a1); + if (f != null) + { + FriendsService.Delete(a2, f.Friend); + // and also the converse + FriendsService.Delete(f.Friend, a2.ToString()); + } + } + } + protected override bool DeleteFriendship(UUID agentID, UUID exfriendID) { Boolean agentIsLocal = true; @@ -684,5 +681,74 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends friendConn.DeleteFriendship(foreignUser, localUser, secret); } } + + protected override bool ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im) + { + if (base.ForwardFriendshipOffer(agentID, friendID, im)) + return true; + + // OK, that didn't work, so let's try to find this user somewhere + if (!m_uMan.IsLocalGridUser(friendID)) + { + string friendsURL = m_uMan.GetUserServerURL(friendID, "FriendsServerURI"); + if (friendsURL != string.Empty) + { + m_log.DebugFormat("[HGFRIENDS MODULE]: Forwading friendship from {0} to {1} @ {2}", agentID, friendID, friendsURL); + GridRegion region = new GridRegion(); + region.ServerURI = friendsURL; + + string name = im.fromAgentName; + if (m_uMan.IsLocalGridUser(agentID)) + { + IClientAPI agentClient = LocateClientObject(agentID); + AgentCircuitData agentClientCircuit = ((Scene)(agentClient.Scene)).AuthenticateHandler.GetAgentCircuitData(agentClient.CircuitCode); + string agentHomeService = string.Empty; + try + { + agentHomeService = agentClientCircuit.ServiceURLs["HomeURI"].ToString(); + string lastname = "@" + new Uri(agentHomeService).Authority; + string firstname = im.fromAgentName.Replace(" ", "."); + name = firstname + lastname; + } + catch (KeyNotFoundException) + { + m_log.DebugFormat("[HGFRIENDS MODULE]: Key HomeURI not found for user {0}", agentID); + return false; + } + catch (NullReferenceException) + { + m_log.DebugFormat("[HGFRIENDS MODULE]: Null HomeUri for local user {0}", agentID); + return false; + } + catch (UriFormatException) + { + m_log.DebugFormat("[HGFRIENDS MODULE]: Malformed HomeUri {0} for local user {1}", agentHomeService, agentID); + return false; + } + } + + m_HGFriendsConnector.FriendshipOffered(region, agentID, friendID, im.message, name); + + return true; + } + } + + return false; + } + + public override bool LocalFriendshipOffered(UUID toID, GridInstantMessage im) + { + if (base.LocalFriendshipOffered(toID, im)) + { + if (im.fromAgentName.Contains("@")) + { + string[] parts = im.fromAgentName.Split(new char[] { '@' }); + if (parts.Length == 2) + m_uMan.AddUser(new UUID(im.fromAgentID), parts[0], "http://" + parts[1]); + } + return true; + } + return false; + } } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs index 8077a7a53b..4eecaa2080 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/HGUserManagementModule.cs @@ -71,52 +71,90 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement protected override void AddAdditionalUsers(UUID avatarID, string query, List users) { - string[] words = query.Split(new char[] { ' ' }); - - for (int i = 0; i < words.Length; i++) + if (query.Contains("@")) // First.Last@foo.com, maybe? { - if (words[i].Length < 3) + string[] words = query.Split(new char[] { '@' }); + if (words.Length != 2) { - if (i != words.Length - 1) - Array.Copy(words, i + 1, words, i, words.Length - i - 1); - Array.Resize(ref words, words.Length - 1); + m_log.DebugFormat("[USER MANAGEMENT MODULE]: Malformed address {0}", query); + return; } - } - if (words.Length == 0 || words.Length > 2) - return; + words[0] = words[0].Trim(); // it has at least 1 + words[1] = words[1].Trim(); - if (words.Length == 2) // First.Last @foo.com, maybe? - { - bool found = false; + if (words[0] == String.Empty) // query was @foo.com? + { + foreach (UserData d in m_UserCache.Values) + { + if (d.LastName.ToLower().StartsWith("@" + words[1].ToLower())) + users.Add(d); + } + + // We're done + return; + } + + // words.Length == 2 and words[0] != string.empty + // first.last@foo.com ? foreach (UserData d in m_UserCache.Values) { - if (d.LastName.StartsWith("@") && - (d.FirstName.ToLower().Equals(words[0].ToLower()) || - d.LastName.ToLower().Equals(words[1].ToLower()))) + if (d.LastName.StartsWith("@") && + d.FirstName.ToLower().Equals(words[0].ToLower()) && + d.LastName.ToLower().Equals("@" + words[1].ToLower())) { users.Add(d); - found = true; - break; + // It's cached. We're done + return; } } - if (!found) // This is it! Let's ask the other world + + // This is it! Let's ask the other world + if (words[0].Contains(".")) { - // TODO - //UserAgentServiceConnector uasConn = new UserAgentServiceConnector(words[0]); - //uasConn.GetUserInfo(...); - } - } - else - { - foreach (UserData d in m_UserCache.Values) - { - if (d.LastName.StartsWith("@") && - (d.FirstName.ToLower().StartsWith(query.ToLower()) || - d.LastName.ToLower().StartsWith(query.ToLower()))) - users.Add(d); + string[] names = words[0].Split(new char[] { '.' }); + if (names.Length >= 2) + { + + string uriStr = "http://" + words[1]; + // Let's check that the last name is a valid address + try + { + new Uri(uriStr); + } + catch (UriFormatException) + { + m_log.DebugFormat("[USER MANAGEMENT MODULE]: Malformed address {0}", uriStr); + return; + } + + UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr); + UUID userID = uasConn.GetUUID(names[0], names[1]); + if (!userID.Equals(UUID.Zero)) + { + UserData ud = new UserData(); + ud.Id = userID; + ud.FirstName = words[0]; + ud.LastName = "@" + words[1]; + users.Add(ud); + AddUser(userID, names[0], names[1], uriStr); + m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0}@{1} found", words[0], words[1]); + } + else + m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0}@{1} not found", words[0], words[1]); + } } } + //else + //{ + // foreach (UserData d in m_UserCache.Values) + // { + // if (d.LastName.StartsWith("@") && + // (d.FirstName.ToLower().StartsWith(query.ToLower()) || + // d.LastName.ToLower().StartsWith(query.ToLower()))) + // users.Add(d); + // } + //} } } diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index 23ef0fc98d..039747812d 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs @@ -183,7 +183,6 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement List users = new List(); if (accs != null) { - m_log.DebugFormat("[USER MANAGEMENT MODULE]: Found {0} users", accs.Count); foreach (UserAccount acc in accs) { UserData ud = new UserData(); @@ -300,7 +299,6 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement public string GetUserName(UUID uuid) { - //m_log.DebugFormat("[XXX] GetUserName {0}", uuid); string[] names = GetUserNames(uuid); if (names.Length == 2) { @@ -341,9 +339,9 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement if (userdata.HomeURL != null && userdata.HomeURL != string.Empty) { - m_log.DebugFormat( - "[USER MANAGEMENT MODULE]: Did not find url type {0} so requesting urls from '{1}' for {2}", - serverType, userdata.HomeURL, userID); + //m_log.DebugFormat( + // "[USER MANAGEMENT MODULE]: Did not find url type {0} so requesting urls from '{1}' for {2}", + // serverType, userdata.HomeURL, userID); UserAgentServiceConnector uConn = new UserAgentServiceConnector(userdata.HomeURL); userdata.ServerURLs = uConn.GetServerURLs(userID); @@ -402,11 +400,15 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement public void AddUser(UUID uuid, string first, string last, string homeURL) { + // m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL); + AddUser(uuid, homeURL + ";" + first + " " + last); } public void AddUser (UUID id, string creatorData) { + //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, creatorData {1}", id, creatorData); + UserData oldUser; //lock the whole block - prevent concurrent update lock (m_UserCache) @@ -432,9 +434,8 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement return; } } -// m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, creatorData {1}", id, creatorData); - UserAccount account = m_Scenes [0].UserAccountService.GetUserAccount (m_Scenes [0].RegionInfo.ScopeID, id); + UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount (m_Scenes [0].RegionInfo.ScopeID, id); if (account != null) { @@ -483,9 +484,9 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement lock (m_UserCache) m_UserCache[user.Id] = user; -// m_log.DebugFormat( -// "[USER MANAGEMENT MODULE]: Added user {0} {1} {2} {3}", -// user.Id, user.FirstName, user.LastName, user.HomeURL); + //m_log.DebugFormat( + // "[USER MANAGEMENT MODULE]: Added user {0} {1} {2} {3}", + // user.Id, user.FirstName, user.LastName, user.HomeURL); } public bool IsLocalGridUser(UUID uuid) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs index 89abbb225b..8df1c7b49c 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs @@ -48,8 +48,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid private static bool m_Enabled = false; private IConfigSource m_Config; - bool m_Registered = false; - GatekeeperServiceInConnector m_HypergridHandler; + private bool m_Registered = false; + private string m_LocalServiceDll = String.Empty; + private GatekeeperServiceInConnector m_HypergridHandler; + private UserAgentServerConnector m_UASHandler; #region IRegionModule interface @@ -63,6 +65,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid if (m_Enabled) { m_log.Info("[HGGRID IN CONNECTOR]: Hypergrid Service In Connector enabled"); + IConfig fconfig = config.Configs["FriendsService"]; + if (fconfig != null) + { + m_LocalServiceDll = fconfig.GetString("LocalServiceModule", m_LocalServiceDll); + if (m_LocalServiceDll == String.Empty) + m_log.WarnFormat("[HGGRID IN CONNECTOR]: Friends LocalServiceModule config missing"); + } } } @@ -91,7 +100,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid { if (!m_Enabled) return; - } public void RemoveRegion(Scene scene) @@ -112,14 +120,20 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid m_log.Info("[HypergridService]: Starting..."); ISimulationService simService = scene.RequestModuleInterface(); + IFriendsSimConnector friendsConn = scene.RequestModuleInterface(); + Object[] args = new Object[] { m_Config }; + IFriendsService friendsService = ServerUtils.LoadPlugin(m_LocalServiceDll, args); + m_HypergridHandler = new GatekeeperServiceInConnector(m_Config, MainServer.Instance, simService); - IFriendsSimConnector friendsConn = scene.RequestModuleInterface(); - new UserAgentServerConnector(m_Config, MainServer.Instance, friendsConn); + m_UASHandler = new UserAgentServerConnector(m_Config, MainServer.Instance, friendsConn); + new HeloServiceInConnector(m_Config, MainServer.Instance, "HeloService"); - new HGFriendsServerConnector(m_Config, MainServer.Instance, "HGFriendsService"); + + new HGFriendsServerConnector(m_Config, MainServer.Instance, "HGFriendsService", friendsConn); } scene.RegisterModuleInterface(m_HypergridHandler.GateKeeper); + scene.RegisterModuleInterface(m_UASHandler.HomeUsersService); } #endregion diff --git a/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs index 3efb7ddbe3..4e6bfb88a1 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs @@ -132,7 +132,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap data.MapImageId = info.TerrainImage; // ugh! V2-3 is very sensitive about the result being // exactly the same as the requested name - if (regionInfos.Count == 1) + if (regionInfos.Count == 1 && mapNameOrig.Contains("|") || mapNameOrig.Contains("+")) data.Name = mapNameOrig; else data.Name = info.RegionName; diff --git a/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs index 82a72206d3..6c79c607af 100644 --- a/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs +++ b/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs @@ -36,36 +36,42 @@ namespace OpenSim.Server.Handlers.Hypergrid { public class HGFriendsServerConnector : ServiceConnector { - private IFriendsService m_FriendsService; private IUserAgentService m_UserAgentService; + private IHGFriendsService m_TheService; private string m_ConfigName = "HGFriendsService"; + // Called from Robust public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName) : - base(config, server, configName) + this(config, server, configName, null) { - if (configName != string.Empty) + + } + + // Called from standalone configurations + public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName, IFriendsSimConnector localConn) + : base(config, server, configName) + { + if (configName != string.Empty) m_ConfigName = configName; + Object[] args = new Object[] { config, m_ConfigName, localConn }; + IConfig serverConfig = config.Configs[m_ConfigName]; if (serverConfig == null) throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); string theService = serverConfig.GetString("LocalServiceModule", String.Empty); - if (theService == String.Empty) throw new Exception("No LocalServiceModule in config file"); - - Object[] args = new Object[] { config }; - m_FriendsService = ServerUtils.LoadPlugin(theService, args); + m_TheService = ServerUtils.LoadPlugin(theService, args); theService = serverConfig.GetString("UserAgentService", string.Empty); if (theService == String.Empty) throw new Exception("No UserAgentService in " + m_ConfigName); + m_UserAgentService = ServerUtils.LoadPlugin(theService, new Object[] { config, localConn }); - m_UserAgentService = ServerUtils.LoadPlugin(theService, args); - - server.AddStreamHandler(new HGFriendsServerPostHandler(m_FriendsService, m_UserAgentService)); + server.AddStreamHandler(new HGFriendsServerPostHandler(m_TheService, m_UserAgentService, localConn)); } } } diff --git a/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs b/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs index 661507e7e4..ca566f2857 100644 --- a/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs +++ b/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs @@ -39,6 +39,7 @@ using System.Collections.Generic; using OpenSim.Server.Base; using OpenSim.Services.Interfaces; using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; using OpenSim.Framework; using OpenSim.Framework.Servers.HttpServer; using OpenMetaverse; @@ -49,15 +50,22 @@ namespace OpenSim.Server.Handlers.Hypergrid { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private IFriendsService m_FriendsService; private IUserAgentService m_UserAgentService; + private IFriendsSimConnector m_FriendsLocalSimConnector; + private IHGFriendsService m_TheService; - public HGFriendsServerPostHandler(IFriendsService service, IUserAgentService uservice) : + public HGFriendsServerPostHandler(IHGFriendsService service, IUserAgentService uas, IFriendsSimConnector friendsConn) : base("POST", "/hgfriends") { - m_FriendsService = service; - m_UserAgentService = uservice; - m_log.DebugFormat("[HGFRIENDS HANDLER]: HGFriendsServerPostHandler is On"); + m_TheService = service; + m_UserAgentService = uas; + m_FriendsLocalSimConnector = friendsConn; + + m_log.DebugFormat("[HGFRIENDS HANDLER]: HGFriendsServerPostHandler is On ({0})", + (m_FriendsLocalSimConnector == null ? "robust" : "standalone")); + + if (m_TheService == null) + m_log.ErrorFormat("[HGFRIENDS HANDLER]: TheService is null!"); } public override byte[] Handle(string path, Stream requestData, @@ -90,6 +98,26 @@ namespace OpenSim.Server.Handlers.Hypergrid case "deletefriendship": return DeleteFriendship(request); + + /* Same as inter-sim */ + case "friendship_offered": + return FriendshipOffered(request); + + case "validate_friendship_offered": + return ValidateFriendshipOffered(request); + /* + case "friendship_approved": + return FriendshipApproved(request); + + case "friendship_denied": + return FriendshipDenied(request); + + case "friendship_terminated": + return FriendshipTerminated(request); + + case "grant_rights": + return GrantRights(request); + */ } m_log.DebugFormat("[HGFRIENDS HANDLER]: unknown method {0} request {1}", method.Length, method); } @@ -126,39 +154,20 @@ namespace OpenSim.Server.Handlers.Hypergrid return FailureResult(); } - FriendInfo[] friendsInfo = m_FriendsService.GetFriends(principalID); - foreach (FriendInfo finfo in friendsInfo) - { - if (finfo.Friend.StartsWith(friendID.ToString())) - return SuccessResult(finfo.TheirFlags.ToString()); - } + int perms = m_TheService.GetFriendPerms(principalID, friendID); + if (perms < 0) + return FailureResult("Friend not found"); - return FailureResult("Friend not found"); + return SuccessResult(perms.ToString()); } byte[] NewFriendship(Dictionary request) { - if (!VerifyServiceKey(request)) - return FailureResult(); + bool verified = VerifyServiceKey(request); - // OK, can proceed FriendInfo friend = new FriendInfo(request); - UUID friendID; - string tmp = string.Empty; - if (!Util.ParseUniversalUserIdentifier(friend.Friend, out friendID, out tmp, out tmp, out tmp, out tmp)) - return FailureResult(); - - m_log.DebugFormat("[HGFRIENDS HANDLER]: New friendship {0} {1}", friend.PrincipalID, friend.Friend); - - // If the friendship already exists, return fail - FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); - foreach (FriendInfo finfo in finfos) - if (finfo.Friend.StartsWith(friendID.ToString())) - return FailureResult(); - - // the user needs to confirm when he gets home - bool success = m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 0); + bool success = m_TheService.NewFriendship(friend, verified); if (success) return SuccessResult(); @@ -174,25 +183,53 @@ namespace OpenSim.Server.Handlers.Hypergrid secret = request["SECRET"].ToString(); if (secret == string.Empty) - return FailureResult(); + return BoolResult(false); - FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); - foreach (FriendInfo finfo in finfos) - { - // We check the secret here - if (finfo.Friend.StartsWith(friend.Friend) && finfo.Friend.EndsWith(secret)) - { - m_log.DebugFormat("[HGFRIENDS HANDLER]: Delete friendship {0} {1}", friend.PrincipalID, friend.Friend); - m_FriendsService.Delete(friend.PrincipalID, finfo.Friend); - m_FriendsService.Delete(finfo.Friend, friend.PrincipalID.ToString()); + bool success = m_TheService.DeleteFriendship(friend, secret); - return SuccessResult(); - } - } - - return FailureResult(); + return BoolResult(success); } + byte[] FriendshipOffered(Dictionary request) + { + UUID fromID = UUID.Zero; + UUID toID = UUID.Zero; + string message = string.Empty; + string name = string.Empty; + + m_log.DebugFormat("[HGFRIENDS HANDLER]: Friendship offered"); + if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID")) + return BoolResult(false); + + if (!UUID.TryParse(request["ToID"].ToString(), out toID)) + return BoolResult(false); + + message = request["Message"].ToString(); + + if (!UUID.TryParse(request["FromID"].ToString(), out fromID)) + return BoolResult(false); + + if (request.ContainsKey("FromName")) + name = request["FromName"].ToString(); + + bool success = m_TheService.FriendshipOffered(fromID, name, toID, message); + + return BoolResult(success); + } + + byte[] ValidateFriendshipOffered(Dictionary request) + { + FriendInfo friend = new FriendInfo(request); + UUID friendID = UUID.Zero; + if (!UUID.TryParse(friend.Friend, out friendID)) + return BoolResult(false); + + bool success = m_TheService.ValidateFriendshipOffered(friend.PrincipalID, friendID); + + return BoolResult(success); + } + + #endregion #region Misc @@ -205,10 +242,15 @@ namespace OpenSim.Server.Handlers.Hypergrid return false; } + if (request["KEY"] == null || request["SESSIONID"] == null) + return false; + string serviceKey = request["KEY"].ToString(); string sessionStr = request["SESSIONID"].ToString(); + UUID sessionID; - UUID.TryParse(sessionStr, out sessionID); + if (!UUID.TryParse(sessionStr, out sessionID) || serviceKey == string.Empty) + return false; if (!m_UserAgentService.VerifyAgent(sessionID, serviceKey)) { @@ -256,7 +298,7 @@ namespace OpenSim.Server.Handlers.Hypergrid doc.AppendChild(rootElement); - XmlElement result = doc.CreateElement("", "Result", ""); + XmlElement result = doc.CreateElement("", "RESULT", ""); result.AppendChild(doc.CreateTextNode("Success")); rootElement.AppendChild(result); @@ -289,7 +331,7 @@ namespace OpenSim.Server.Handlers.Hypergrid doc.AppendChild(rootElement); - XmlElement result = doc.CreateElement("", "Result", ""); + XmlElement result = doc.CreateElement("", "RESULT", ""); result.AppendChild(doc.CreateTextNode("Failure")); rootElement.AppendChild(result); @@ -302,6 +344,28 @@ namespace OpenSim.Server.Handlers.Hypergrid return DocToBytes(doc); } + private byte[] BoolResult(bool value) + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "ServerResponse", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "RESULT", ""); + result.AppendChild(doc.CreateTextNode(value.ToString())); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + private byte[] DocToBytes(XmlDocument doc) { MemoryStream ms = new MemoryStream(); @@ -313,6 +377,7 @@ namespace OpenSim.Server.Handlers.Hypergrid return ms.ToArray(); } + #endregion } } diff --git a/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs index 1bd37062db..9a0e27e2f1 100644 --- a/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs +++ b/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs @@ -52,6 +52,11 @@ namespace OpenSim.Server.Handlers.Hypergrid // MethodBase.GetCurrentMethod().DeclaringType); private IUserAgentService m_HomeUsersService; + public IUserAgentService HomeUsersService + { + get { return m_HomeUsersService; } + } + private string[] m_AuthorizedCallers; private bool m_VerifyCallers = false; @@ -96,6 +101,7 @@ namespace OpenSim.Server.Handlers.Hypergrid server.AddXmlRPCHandler("locate_user", LocateUser, false); server.AddXmlRPCHandler("get_uui", GetUUI, false); + server.AddXmlRPCHandler("get_uuid", GetUUID, false); server.AddHTTPHandler("/homeagent/", new HomeAgentHandler(m_HomeUsersService, loginServerIP, proxy).Handler); } @@ -410,8 +416,7 @@ namespace OpenSim.Server.Handlers.Hypergrid } /// - /// Locates the user. - /// This is a sensitive operation, only authorized IP addresses can perform it. + /// Returns the UUI of a user given a UUID. /// /// /// @@ -445,5 +450,33 @@ namespace OpenSim.Server.Handlers.Hypergrid } + /// + /// Gets the UUID of a user given First name, Last name. + /// + /// + /// + /// + public XmlRpcResponse GetUUID(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + if (requestData.ContainsKey("first") && requestData.ContainsKey("last")) + { + UUID userID = UUID.Zero; + string first = (string)requestData["first"]; + + string last = (string)requestData["last"]; + UUID uuid = m_HomeUsersService.GetUUID(first, last); + hash["UUID"] = uuid.ToString(); + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } } } diff --git a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs index eea9853463..3fd0c53633 100644 --- a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs +++ b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs @@ -43,7 +43,17 @@ namespace OpenSim.Services.Connectors.Friends { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + protected virtual string ServicePath() + { + return "friends"; + } + public bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message) + { + return FriendshipOffered(region, userID, friendID, message, String.Empty); + } + + public virtual bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message, string userName) { Dictionary sendData = new Dictionary(); //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); @@ -53,9 +63,10 @@ namespace OpenSim.Services.Connectors.Friends sendData["FromID"] = userID.ToString(); sendData["ToID"] = friendID.ToString(); sendData["Message"] = message; + if (userName != String.Empty) + sendData["FromName"] = userName; return Call(region, sendData); - } public bool FriendshipApproved(GridRegion region, UUID userID, string userName, UUID friendID) @@ -138,8 +149,11 @@ namespace OpenSim.Services.Connectors.Friends if (region == null) return false; - m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: region: {0}", region.ExternalHostName + ":" + region.HttpPort); - string uri = "http://" + region.ExternalHostName + ":" + region.HttpPort + "/friends"; + string path = ServicePath(); + if (!region.ServerURI.EndsWith("/")) + path = "/" + path; + string uri = region.ServerURI + path; + m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri); try { diff --git a/OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs index af4b0daf9b..e3f326037c 100644 --- a/OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs +++ b/OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs @@ -40,7 +40,7 @@ using OpenMetaverse; namespace OpenSim.Services.Connectors.Hypergrid { - public class HGFriendsServicesConnector + public class HGFriendsServicesConnector : FriendsSimConnector { private static readonly ILog m_log = LogManager.GetLogger( @@ -66,6 +66,11 @@ namespace OpenSim.Services.Connectors.Hypergrid m_SessionID = sessionID; } + protected override string ServicePath() + { + return "hgfriends"; + } + #region IFriendsService public uint GetFriendPerms(UUID PrincipalID, UUID friendID) @@ -187,23 +192,69 @@ namespace OpenSim.Services.Connectors.Hypergrid { Dictionary replyData = ServerUtils.ParseXmlResponse(reply); - if ((replyData != null) && replyData.ContainsKey("Result") && (replyData["Result"] != null)) + if (replyData.ContainsKey("RESULT")) { - bool success = false; - Boolean.TryParse(replyData["Result"].ToString(), out success); - return success; + if (replyData["RESULT"].ToString().ToLower() == "true") + return true; + else + return false; } else - m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Delete {0} {1} received null response", - PrincipalID, Friend); + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: reply data does not contain result field"); + } else - m_log.DebugFormat("[HGFRIENDS CONNECTOR]: DeleteFriend received null reply"); + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: received empty reply"); return false; } + public bool ValidateFriendshipOffered(UUID fromID, UUID toID) + { + FriendInfo finfo = new FriendInfo(); + finfo.PrincipalID = fromID; + finfo.Friend = toID.ToString(); + + Dictionary sendData = finfo.ToKeyValuePairs(); + + sendData["METHOD"] = "validate_friendship_offered"; + + string reply = string.Empty; + string uri = m_ServerURI + "/hgfriends"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData)); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + return false; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("RESULT")) + { + if (replyData["RESULT"].ToString().ToLower() == "true") + return true; + else + return false; + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: reply data does not contain result field"); + + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: received empty reply"); + + return false; + + } #endregion } } \ No newline at end of file diff --git a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs index d617aee04b..c07c6a6aa7 100644 --- a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs +++ b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs @@ -787,13 +787,72 @@ namespace OpenSim.Services.Connectors.Hypergrid } catch { - m_log.ErrorFormat("[USER AGENT CONNECTOR]: Got exception on LocateUser response."); + m_log.ErrorFormat("[USER AGENT CONNECTOR]: Got exception on GetUUI response."); // reason = "Exception: " + e.Message; } return uui; } + public UUID GetUUID(String first, String last) + { + Hashtable hash = new Hashtable(); + hash["first"] = first; + hash["last"] = last; + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("get_uuid", paramList); + // string reason = string.Empty; + + // Send and get reply + UUID uuid = UUID.Zero; + XmlRpcResponse response = null; + try + { + response = request.Send(m_ServerURL, 10000); + } + catch + { + m_log.DebugFormat("[USER AGENT CONNECTOR]: Unable to contact remote server {0} for GetUUID", m_ServerURL); + // reason = "Exception: " + e.Message; + return uuid; + } + + if (response.IsFault) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: remote call to {0} for GetUUID returned an error: {1}", m_ServerURL, response.FaultString); + // reason = "XMLRPC Fault"; + return uuid; + } + + hash = (Hashtable)response.Value; + //foreach (Object o in hash) + // m_log.Debug(">> " + ((DictionaryEntry)o).Key + ":" + ((DictionaryEntry)o).Value); + try + { + if (hash == null) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: GetUUDI Got null response from {0}! THIS IS BAAAAD", m_ServerURL); + // reason = "Internal error 1"; + return uuid; + } + + // Here's the actual response + if (hash.ContainsKey("UUID")) + UUID.TryParse(hash["UUID"].ToString(), out uuid); + + } + catch + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: Got exception on UUID response."); + // reason = "Exception: " + e.Message; + } + + return uuid; + } + private bool GetBoolResponse(XmlRpcRequest request, out string reason) { //m_log.Debug("[USER AGENT CONNECTOR]: GetBoolResponse from/to " + m_ServerURL); diff --git a/OpenSim/Services/HypergridService/HGFriendsService.cs b/OpenSim/Services/HypergridService/HGFriendsService.cs new file mode 100644 index 0000000000..19ee3e27f9 --- /dev/null +++ b/OpenSim/Services/HypergridService/HGFriendsService.cs @@ -0,0 +1,301 @@ +/* + * 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 System; +using System.Collections.Generic; +using System.Net; +using System.Reflection; + +using OpenSim.Framework; +using OpenSim.Services.Connectors.Friends; +using OpenSim.Services.Connectors.Hypergrid; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +using OpenMetaverse; +using log4net; +using Nini.Config; + +namespace OpenSim.Services.HypergridService +{ + /// + /// W2W social networking + /// + public class HGFriendsService : IHGFriendsService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + static bool m_Initialized = false; + + protected static IGridUserService m_GridUserService; + protected static IGridService m_GridService; + protected static IGatekeeperService m_GatekeeperService; + protected static IFriendsService m_FriendsService; + protected static IPresenceService m_PresenceService; + protected static IUserAccountService m_UserAccountService; + protected static IFriendsSimConnector m_FriendsLocalSimConnector; // standalone, points to HGFriendsModule + protected static FriendsSimConnector m_FriendsSimConnector; // grid + + private static string m_ConfigName = "HGFriendsService"; + + public HGFriendsService(IConfigSource config, String configName, IFriendsSimConnector localSimConn) + { + if (m_FriendsLocalSimConnector == null) + m_FriendsLocalSimConnector = localSimConn; + + if (!m_Initialized) + { + m_Initialized = true; + + if (configName != String.Empty) + m_ConfigName = configName; + + Object[] args = new Object[] { config }; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string theService = serverConfig.GetString("FriendsService", string.Empty); + if (theService == String.Empty) + throw new Exception("No FriendsService in config file " + m_ConfigName); + m_FriendsService = ServerUtils.LoadPlugin(theService, args); + + theService = serverConfig.GetString("UserAccountService", string.Empty); + if (theService == String.Empty) + throw new Exception("No UserAccountService in " + m_ConfigName); + m_UserAccountService = ServerUtils.LoadPlugin(theService, args); + + theService = serverConfig.GetString("GridService", string.Empty); + if (theService == String.Empty) + throw new Exception("No GridService in " + m_ConfigName); + m_GridService = ServerUtils.LoadPlugin(theService, args); + + theService = serverConfig.GetString("PresenceService", string.Empty); + if (theService == String.Empty) + throw new Exception("No PresenceService in " + m_ConfigName); + m_PresenceService = ServerUtils.LoadPlugin(theService, args); + + m_FriendsSimConnector = new FriendsSimConnector(); + + m_log.DebugFormat("[HGFRIENDS SERVICE]: Starting..."); + + } + } + + #region IHGFriendsService + + public int GetFriendPerms(UUID userID, UUID friendID) + { + FriendInfo[] friendsInfo = m_FriendsService.GetFriends(userID); + foreach (FriendInfo finfo in friendsInfo) + { + if (finfo.Friend.StartsWith(friendID.ToString())) + return finfo.TheirFlags; + } + return -1; + } + + public bool NewFriendship(FriendInfo friend, bool verified) + { + UUID friendID; + string tmp = string.Empty, url = String.Empty, first = String.Empty, last = String.Empty; + if (!Util.ParseUniversalUserIdentifier(friend.Friend, out friendID, out url, out first, out last, out tmp)) + return false; + + m_log.DebugFormat("[HGFRIENDS SERVICE]: New friendship {0} {1} ({2})", friend.PrincipalID, friend.Friend, verified); + + // Does the friendship already exist? + FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); + foreach (FriendInfo finfo in finfos) + { + if (finfo.Friend.StartsWith(friendID.ToString())) + return false; + } + // Verified user session. But the user needs to confirm friendship when he gets home + if (verified) + return m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 0); + + // Does the reverted friendship exist? meaning that this user initiated the request + finfos = m_FriendsService.GetFriends(friendID); + bool userInitiatedOffer = false; + foreach (FriendInfo finfo in finfos) + { + if (friend.Friend.StartsWith(finfo.PrincipalID.ToString()) && finfo.Friend.StartsWith(friend.PrincipalID.ToString()) && finfo.TheirFlags == -1) + { + userInitiatedOffer = true; + // Let's delete the existing friendship relations that was stored + m_FriendsService.Delete(friendID, finfo.Friend); + break; + } + } + + if (userInitiatedOffer) + { + m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 1); + m_FriendsService.StoreFriend(friend.Friend, friend.PrincipalID.ToString(), 1); + // notify the user + ForwardToSim("ApproveFriendshipRequest", friendID, Util.UniversalName(first, last, url), "", friend.PrincipalID, ""); + return true; + } + return false; + } + + public bool DeleteFriendship(FriendInfo friend, string secret) + { + FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); + foreach (FriendInfo finfo in finfos) + { + // We check the secret here. Or if the friendship request was initiated here, and was declined + if (finfo.Friend.StartsWith(friend.Friend) && finfo.Friend.EndsWith(secret)) + { + m_log.DebugFormat("[HGFRIENDS SERVICE]: Delete friendship {0} {1}", friend.PrincipalID, friend.Friend); + m_FriendsService.Delete(friend.PrincipalID, finfo.Friend); + m_FriendsService.Delete(finfo.Friend, friend.PrincipalID.ToString()); + + return true; + } + } + + return false; + } + + public bool FriendshipOffered(UUID fromID, string fromName, UUID toID, string message) + { + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, toID); + if (account == null) + return false; + + // OK, we have that user here. + // So let's send back the call, but start a thread to continue + // with the verification and the actual action. + + Util.FireAndForget(delegate { ProcessFriendshipOffered(fromID, fromName, toID, message); }); + + return true; + } + + public bool ValidateFriendshipOffered(UUID fromID, UUID toID) + { + FriendInfo[] finfos = m_FriendsService.GetFriends(toID.ToString()); + foreach (FriendInfo fi in finfos) + { + if (fi.Friend.StartsWith(fromID.ToString()) && fi.TheirFlags == -1) + return true; + } + return false; + } + + #endregion IHGFriendsService + + #region Aux + + private void ProcessFriendshipOffered(UUID fromID, String fromName, UUID toID, String message) + { + // Great, it's a genuine request. Let's proceed. + // But now we need to confirm that the requester is who he says he is + // before we act on the friendship request. + + if (!fromName.Contains("@")) + return; + + string[] parts = fromName.Split(new char[] {'@'}); + if (parts.Length != 2) + return; + + string uriStr = "http://" + parts[1]; + try + { + new Uri(uriStr); + } + catch (UriFormatException) + { + return; + } + + UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr); + Dictionary servers = uasConn.GetServerURLs(fromID); + if (!servers.ContainsKey("FriendsServerURI")) + return; + + HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(servers["FriendsServerURI"].ToString()); + if (!friendsConn.ValidateFriendshipOffered(fromID, toID)) + { + m_log.WarnFormat("[HGFRIENDS SERVICE]: Friendship request from {0} to {1} is invalid. Impersonations?", fromID, toID); + return; + } + + string fromUUI = Util.UniversalIdentifier(fromID, parts[0], "@" + parts[1], uriStr); + // OK, we're good! + ForwardToSim("FriendshipOffered", fromID, fromName, fromUUI, toID, message); + } + + private bool ForwardToSim(string op, UUID fromID, string name, String fromUUI, UUID toID, string message) + { + PresenceInfo session = null; + GridRegion region = null; + PresenceInfo[] sessions = m_PresenceService.GetAgents(new string[] { toID.ToString() }); + if (sessions != null && sessions.Length > 0) + session = sessions[0]; + if (session != null) + region = m_GridService.GetRegionByUUID(UUID.Zero, session.RegionID); + + switch (op) + { + case "FriendshipOffered": + // Let's store backwards + string secret = UUID.Random().ToString().Substring(0, 8); + m_FriendsService.StoreFriend(toID.ToString(), fromUUI + ";" + secret, 0); + if (m_FriendsLocalSimConnector != null) // standalone + { + GridInstantMessage im = new GridInstantMessage(null, fromID, name, toID, + (byte)InstantMessageDialog.FriendshipOffered, message, false, Vector3.Zero); + // !! HACK + im.imSessionID = im.fromAgentID; + return m_FriendsLocalSimConnector.LocalFriendshipOffered(toID, im); + } + else if (region != null) // grid + return m_FriendsSimConnector.FriendshipOffered(region, fromID, toID, message, name); + break; + case "ApproveFriendshipRequest": + if (m_FriendsLocalSimConnector != null) // standalone + return m_FriendsLocalSimConnector.LocalFriendshipApproved(fromID, name, toID); + else if (region != null) //grid + return m_FriendsSimConnector.FriendshipApproved(region, fromID, name, toID); + break; + } + + return false; + } + + #endregion Aux + } +} diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 1a839f3338..65963d9820 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -564,6 +564,16 @@ namespace OpenSim.Services.HypergridService return string.Empty; } + + public UUID GetUUID(String first, String last) + { + // Let's see if it's a local user + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, first, last); + if (account != null) + return account.PrincipalID; + else + return UUID.Zero; + } } class TravelingAgentInfo diff --git a/OpenSim/Services/Interfaces/IHypergridServices.cs b/OpenSim/Services/Interfaces/IHypergridServices.cs index 5b293acbee..f48b8a9a76 100644 --- a/OpenSim/Services/Interfaces/IHypergridServices.cs +++ b/OpenSim/Services/Interfaces/IHypergridServices.cs @@ -62,6 +62,8 @@ namespace OpenSim.Services.Interfaces // on behalf of the userID string GetUUI(UUID userID, UUID targetUserID); + UUID GetUUID(String first, String last); + // Returns the local friends online List StatusNotification(List friends, UUID userID, bool online); //List GetOnlineFriends(UUID userID, List friends); @@ -79,6 +81,17 @@ namespace OpenSim.Services.Interfaces public interface IFriendsSimConnector { bool StatusNotify(UUID userID, UUID friendID, bool online); + bool LocalFriendshipOffered(UUID toID, GridInstantMessage im); + bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID); + } + + public interface IHGFriendsService + { + int GetFriendPerms(UUID userID, UUID friendID); + bool NewFriendship(FriendInfo finfo, bool verified); + bool DeleteFriendship(FriendInfo finfo, string secret); + bool FriendshipOffered(UUID from, string fromName, UUID to, string message); + bool ValidateFriendshipOffered(UUID fromID, UUID toID); } public interface IInstantMessageSimConnector diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini index ee51067fed..75c478803b 100644 --- a/bin/config-include/StandaloneHypergrid.ini +++ b/bin/config-include/StandaloneHypergrid.ini @@ -159,8 +159,12 @@ UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" [HGFriendsService] - LocalServiceModule = "OpenSim.Services.FriendsService.dll:FriendsService" + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGFriendsService" UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridService = "OpenSim.Services.GridService.dll:GridService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" [HGInstantMessageService] LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInstantMessageService"