From d21e9c755f004d8fe03b11bc57b810dbd401435a Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 19 May 2011 16:54:46 -0700 Subject: [PATCH] HG Friends working to some extent: friendships offered and accepted correctly handled. Friends list showing correct foreign names. TODO: GrantRights. --- OpenSim/Data/IFriendsData.cs | 2 +- .../MySQL/Resources/FriendsStore.migrations | 6 + OpenSim/Data/Null/NullFriendsData.cs | 4 +- OpenSim/Framework/AgentCircuitData.cs | 1 + OpenSim/Framework/AssetBase.cs | 6 + OpenSim/Framework/Util.cs | 61 +++ .../Avatar/Friends/FriendsModule.cs | 153 ++++-- .../Avatar/Friends/HGFriendsModule.cs | 438 ++++++++++++++++++ .../UserManagement/UserManagementModule.cs | 2 + .../HypergridServiceInConnectorModule.cs | 1 + .../Handlers/Friends/FriendServerConnector.cs | 6 +- .../Friends/FriendsServerPostHandler.cs | 2 +- .../Hypergrid/HGFriendServerConnector.cs | 71 +++ .../Hypergrid/HGFriendsServerPostHandler.cs | 242 ++++++++++ .../Friends/FriendsServiceConnector.cs | 13 +- .../Hypergrid/HGFriendsServiceConnector.cs | 175 +++++++ .../SimianFriendsServiceConnector.cs | 2 +- OpenSim/Services/Friends/FriendsService.cs | 9 +- .../HypergridService/HGFriendsService.cs | 95 ++++ .../Services/Interfaces/IFriendsService.cs | 2 +- .../LLLoginService/LLLoginResponse.cs | 14 +- bin/config-include/StandaloneHypergrid.ini | 5 + prebuild.xml | 1 + 23 files changed, 1253 insertions(+), 58 deletions(-) create mode 100644 OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs create mode 100644 OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs create mode 100644 OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs create mode 100644 OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs create mode 100644 OpenSim/Services/HypergridService/HGFriendsService.cs diff --git a/OpenSim/Data/IFriendsData.cs b/OpenSim/Data/IFriendsData.cs index 1f1a0316f6..4da567e56b 100644 --- a/OpenSim/Data/IFriendsData.cs +++ b/OpenSim/Data/IFriendsData.cs @@ -34,7 +34,7 @@ namespace OpenSim.Data { public class FriendsData { - public UUID PrincipalID; + public string PrincipalID; public string Friend; public Dictionary Data; } diff --git a/OpenSim/Data/MySQL/Resources/FriendsStore.migrations b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations index ce713bdb89..35e5e93cde 100644 --- a/OpenSim/Data/MySQL/Resources/FriendsStore.migrations +++ b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations @@ -21,5 +21,11 @@ INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userf COMMIT; +:VERSION 3 # ------------------------- +BEGIN; + +ALTER TABLE `Friends` MODIFY COLUMN PrincipalID varchar(255) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/Null/NullFriendsData.cs b/OpenSim/Data/Null/NullFriendsData.cs index e7f7fd3738..0c69bb1700 100644 --- a/OpenSim/Data/Null/NullFriendsData.cs +++ b/OpenSim/Data/Null/NullFriendsData.cs @@ -53,7 +53,7 @@ namespace OpenSim.Data.Null { List lst = m_Data.FindAll(delegate (FriendsData fdata) { - return fdata.PrincipalID == userID; + return fdata.PrincipalID == userID.ToString(); }); if (lst != null) @@ -74,7 +74,7 @@ namespace OpenSim.Data.Null public bool Delete(UUID userID, string friendID) { - List lst = m_Data.FindAll(delegate(FriendsData fdata) { return fdata.PrincipalID == userID; }); + List lst = m_Data.FindAll(delegate(FriendsData fdata) { return fdata.PrincipalID == userID.ToString(); }); if (lst != null) { FriendsData friend = lst.Find(delegate(FriendsData fdata) { return fdata.Friend == friendID; }); diff --git a/OpenSim/Framework/AgentCircuitData.cs b/OpenSim/Framework/AgentCircuitData.cs index dbd47d392a..125910e5bc 100644 --- a/OpenSim/Framework/AgentCircuitData.cs +++ b/OpenSim/Framework/AgentCircuitData.cs @@ -345,6 +345,7 @@ namespace OpenSim.Framework } } } + } diff --git a/OpenSim/Framework/AssetBase.cs b/OpenSim/Framework/AssetBase.cs index 53d28be088..5f68cdaabf 100644 --- a/OpenSim/Framework/AssetBase.cs +++ b/OpenSim/Framework/AssetBase.cs @@ -215,6 +215,12 @@ namespace OpenSim.Framework set { m_metadata.Temporary = value; } } + public string CreatorID + { + get { return m_metadata.CreatorID; } + set { m_metadata.CreatorID = value; } + } + public AssetFlags Flags { get { return m_metadata.Flags; } diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index aaa2724532..af21cb55cc 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1694,5 +1694,66 @@ namespace OpenSim.Framework return (T)Enum.Parse(typeof(T), value); ; } #endregion + + #region Universal User Identifiers + /// + /// + /// uuid[;endpoint[;name]] + /// + /// + /// + /// + public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname) + { + uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; + + string[] parts = value.Split(';'); + if (parts.Length >= 1) + if (!UUID.TryParse(parts[0], out uuid)) + return false; + + if (parts.Length >= 2) + url = parts[1]; + + if (parts.Length >= 3) + { + string[] name = parts[2].Split(); + if (name.Length == 2) + { + firstname = name[0]; + lastname = name[1]; + } + } + + return true; + } + + /// + /// + /// + /// + /// uuid[;endpoint[;name]] + 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; + } + else + return acircuit.AgentID.ToString(); + } + #endregion } } diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index 5baf0785f4..21cd924583 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -49,6 +49,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends { public class FriendsModule : ISharedRegionModule, IFriendsModule { + protected bool m_Enabled = false; + protected class UserFriendData { public UUID PrincipalID; @@ -130,7 +132,25 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends } } + #region ISharedRegionModule public void Initialise(IConfigSource config) + { + IConfig moduleConfig = config.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("FriendsModule", "FriendsModule"); + m_log.DebugFormat("[XXX] {0} compared to {1}", name, Name); + if (name == Name) + { + InitModule(config); + + m_Enabled = true; + m_log.InfoFormat("[FRIENDS MODULE]: {0} enabled.", Name); + } + } + } + + protected void InitModule(IConfigSource config) { IConfig friendsConfig = config.Configs["Friends"]; if (friendsConfig != null) @@ -153,7 +173,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends m_log.Error("[FRIENDS]: No Connector defined in section Friends, or failed to load, cannot continue"); throw new Exception("Connector load error"); } - } public void PostInitialise() @@ -166,6 +185,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends public void AddRegion(Scene scene) { + if (!m_Enabled) + return; + m_Scenes.Add(scene); scene.RegisterModuleInterface(this); @@ -181,10 +203,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends public void RemoveRegion(Scene scene) { + if (!m_Enabled) + return; + m_Scenes.Remove(scene); } - public string Name + public virtual string Name { get { return "FriendsModule"; } } @@ -194,6 +219,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends get { return null; } } + #endregion + public uint GetFriendPerms(UUID principalID, UUID friendID) { FriendInfo[] friends = GetFriends(principalID); @@ -214,30 +241,33 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends client.OnTerminateFriendship += OnTerminateFriendship; client.OnGrantUserRights += OnGrantUserRights; - // Asynchronously fetch the friends list or increment the refcount for the existing - // friends list - Util.FireAndForget( - delegate(object o) - { - lock (m_Friends) - { - UserFriendData friendsData; - if (m_Friends.TryGetValue(client.AgentId, out friendsData)) - { - friendsData.Refcount++; - } - else - { - friendsData = new UserFriendData(); - friendsData.PrincipalID = client.AgentId; - friendsData.Friends = FriendsService.GetFriends(client.AgentId); - friendsData.Refcount = 1; + Util.FireAndForget(delegate { FetchFriendslist(client.AgentId); }); + } - m_Friends[client.AgentId] = friendsData; - } - } + /// Fetch the friends list or increment the refcount for the existing + /// friends list + /// Returns true if the list was fetched, false if it wasn't + protected virtual bool FetchFriendslist(UUID agentID) + { + lock (m_Friends) + { + UserFriendData friendsData; + if (m_Friends.TryGetValue(agentID, out friendsData)) + { + friendsData.Refcount++; + return false; } - ); + else + { + friendsData = new UserFriendData(); + friendsData.PrincipalID = agentID; + friendsData.Friends = FriendsService.GetFriends(agentID); + friendsData.Refcount = 1; + + m_Friends[agentID] = friendsData; + return true; + } + } } private void OnClientClosed(UUID agentID, Scene scene) @@ -313,10 +343,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends foreach (string fid in outstanding) { UUID fromAgentID; - if (!UUID.TryParse(fid, out fromAgentID)) + string firstname = "Unknown", lastname = "User"; + if (!GetAgentInfo(client.Scene.RegionInfo.ScopeID, fid, out fromAgentID, out firstname, out lastname)) + { + m_log.DebugFormat("[FRIENDS MODULE]: skipping malformed friend {0}", fid); continue; - - UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(client.Scene.RegionInfo.ScopeID, fromAgentID); + } PresenceInfo presence = null; PresenceInfo[] presences = PresenceService.GetAgents(new string[] { fid }); @@ -326,15 +358,37 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends im.offline = 0; im.fromAgentID = fromAgentID.Guid; - im.fromAgentName = account.FirstName + " " + account.LastName; + im.fromAgentName = firstname + " " + lastname; im.offline = (byte)((presence == null) ? 1 : 0); im.imSessionID = im.fromAgentID; + im.message = FriendshipMessage(fid); // Finally LocalFriendshipOffered(agentID, im); } } + protected virtual string FriendshipMessage(string friendID) + { + return "Will you be my friend?"; + } + + protected virtual bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last) + { + first = "Unknown"; last = "User"; + if (!UUID.TryParse(fid, out agentID)) + return false; + + UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(scopeID, agentID); + if (account != null) + { + first = account.FirstName; + last = account.LastName; + } + + return true; + } + List GetOnlineFriends(UUID userID) { List friendList = new List(); @@ -475,23 +529,26 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // This user wants to be friends with the other user. // Let's add the relation backwards, in case the other is not online - FriendsService.StoreFriend(friendID, principalID.ToString(), 0); + StoreBackwards(friendID, principalID); // Now let's ask the other user to be friends with this user ForwardFriendshipOffer(principalID, friendID, im); } } + protected virtual void StoreBackwards(UUID friendID, UUID agentID) + { + FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0); + } + private void 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 im.imSessionID = im.fromAgentID; + im.fromAgentName = GetFriendshipRequesterName(agentID); - // Try the local sim - UserAccount account = UserAccountService.GetUserAccount(UUID.Zero, agentID); - im.fromAgentName = (account == null) ? "Unknown" : account.FirstName + " " + account.LastName; - + // Try the local sim if (LocalFriendshipOffered(friendID, im)) return; @@ -509,12 +566,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // If the prospective friend is not online, he'll get the message upon login. } + protected virtual string GetFriendshipRequesterName(UUID agentID) + { + UserAccount account = UserAccountService.GetUserAccount(UUID.Zero, agentID); + return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName; + } + private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List callingCardFolders) { m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", agentID, friendID); - - FriendsService.StoreFriend(agentID, friendID.ToString(), 1); - FriendsService.StoreFriend(friendID, agentID.ToString(), 1); + + StoreFriendships(agentID, friendID); // Update the local cache UpdateFriendsCache(agentID); @@ -544,6 +606,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends } } + protected virtual void StoreFriendships(UUID agentID, UUID friendID) + { + FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), 1); + FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 1); + } + private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List callingCardFolders) { m_log.DebugFormat("[FRIENDS]: {0} denied friendship to {1}", agentID, friendID); @@ -576,8 +644,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID) { - FriendsService.Delete(agentID, exfriendID.ToString()); - FriendsService.Delete(exfriendID, agentID.ToString()); + DeleteFriendship(agentID, exfriendID); // Update local cache UpdateFriendsCache(agentID); @@ -604,6 +671,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends } } + protected virtual void DeleteFriendship(UUID agentID, UUID exfriendID) + { + FriendsService.Delete(agentID, exfriendID.ToString()); + FriendsService.Delete(exfriendID, agentID.ToString()); + } + private void OnGrantUserRights(IClientAPI remoteClient, UUID requester, UUID target, int rights) { FriendInfo[] friends = GetFriends(remoteClient.AgentId); @@ -622,7 +695,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends if (friend != null) // Found it { // Store it on the DB - FriendsService.StoreFriend(requester, target.ToString(), rights); + FriendsService.StoreFriend(requester.ToString(), target.ToString(), rights); // Store it in the local cache int myFlags = friend.MyFlags; @@ -780,7 +853,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends #endregion - private FriendInfo[] GetFriends(UUID agentID) + protected FriendInfo[] GetFriends(UUID agentID) { UserFriendData friendsData; diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs new file mode 100644 index 0000000000..645ecdc011 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs @@ -0,0 +1,438 @@ +/* + * 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; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using Mono.Addins; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.Hypergrid; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Region.CoreModules.Avatar.Friends +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class HGFriendsModule : FriendsModule, ISharedRegionModule, IFriendsModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region ISharedRegionModule + public override string Name + { + get { return "HGFriendsModule"; } + } + + #endregion + + //public void SendFriendsOnlineIfNeeded(IClientAPI client) + //{ + // UUID agentID = client.AgentId; + + // // Check if the online friends list is needed + // lock (m_NeedsListOfFriends) + // { + // if (!m_NeedsListOfFriends.Remove(agentID)) + // return; + // } + + // // Send the friends online + // List online = GetOnlineFriends(agentID); + // if (online.Count > 0) + // { + // m_log.DebugFormat("[FRIENDS MODULE]: User {0} in region {1} has {2} friends online", client.AgentId, client.Scene.RegionInfo.RegionName, online.Count); + // client.SendAgentOnline(online.ToArray()); + // } + + // // Send outstanding friendship offers + // List outstanding = new List(); + // FriendInfo[] friends = GetFriends(agentID); + // foreach (FriendInfo fi in friends) + // { + // if (fi.TheirFlags == -1) + // outstanding.Add(fi.Friend); + // } + + // GridInstantMessage im = new GridInstantMessage(client.Scene, UUID.Zero, String.Empty, agentID, (byte)InstantMessageDialog.FriendshipOffered, + // "Will you be my friend?", true, Vector3.Zero); + + // foreach (string fid in outstanding) + // { + // UUID fromAgentID; + // if (!UUID.TryParse(fid, out fromAgentID)) + // continue; + + // UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(client.Scene.RegionInfo.ScopeID, fromAgentID); + + // PresenceInfo presence = null; + // PresenceInfo[] presences = PresenceService.GetAgents(new string[] { fid }); + // if (presences != null && presences.Length > 0) + // presence = presences[0]; + // if (presence != null) + // im.offline = 0; + + // im.fromAgentID = fromAgentID.Guid; + // im.fromAgentName = account.FirstName + " " + account.LastName; + // im.offline = (byte)((presence == null) ? 1 : 0); + // im.imSessionID = im.fromAgentID; + + // // Finally + // LocalFriendshipOffered(agentID, im); + // } + //} + + //List GetOnlineFriends(UUID userID) + //{ + // List friendList = new List(); + // List online = new List(); + + // FriendInfo[] friends = GetFriends(userID); + // foreach (FriendInfo fi in friends) + // { + // if (((fi.TheirFlags & 1) != 0) && (fi.TheirFlags != -1)) + // friendList.Add(fi.Friend); + // } + + // if (friendList.Count > 0) + // { + // PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray()); + // foreach (PresenceInfo pi in presence) + // { + // UUID presenceID; + // if (UUID.TryParse(pi.UserID, out presenceID)) + // online.Add(presenceID); + // } + // } + + // return online; + //} + + //private void StatusNotify(FriendInfo friend, UUID userID, bool online) + //{ + // UUID friendID; + // if (UUID.TryParse(friend.Friend, out friendID)) + // { + // // Try local + // if (LocalStatusNotification(userID, friendID, online)) + // return; + + // // The friend is not here [as root]. Let's forward. + // PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); + // if (friendSessions != null && friendSessions.Length > 0) + // { + // PresenceInfo friendSession = null; + // foreach (PresenceInfo pinfo in friendSessions) + // if (pinfo.RegionID != UUID.Zero) // let's guard against sessions-gone-bad + // { + // friendSession = pinfo; + // break; + // } + + // if (friendSession != null) + // { + // GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + // //m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName); + // m_FriendsSimConnector.StatusNotify(region, userID, friendID, online); + // } + // } + + // // Friend is not online. Ignore. + // } + // else + // { + // m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friend.Friend); + // } + //} + + protected override bool FetchFriendslist(UUID agentID) + { + if (base.FetchFriendslist(agentID)) + { + // We need to preload the user management cache with the names + // of foreign friends, just like we do with SOPs' creators + foreach (FriendInfo finfo in m_Friends[agentID].Friends) + { + if (finfo.TheirFlags != -1) + { + UUID id; + if (!UUID.TryParse(finfo.Friend, out id)) + { + string url = string.Empty, first = string.Empty, last = string.Empty; + if (Util.ParseUniversalUserIdentifier(finfo.Friend, out id, out url, out first, out last)) + { + IUserManagement uMan = m_Scenes[0].RequestModuleInterface(); + uMan.AddUser(id, url + ";" + first + " " + last); + } + } + } + } + return true; + } + return false; + } + + protected override bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last) + { + first = "Unknown"; last = "User"; + if (base.GetAgentInfo(scopeID, fid, out agentID, out first, out last)) + return true; + + // fid is not a UUID... + string url = string.Empty; + if (Util.ParseUniversalUserIdentifier(fid, out agentID, out url, out first, out last)) + { + IUserManagement userMan = m_Scenes[0].RequestModuleInterface(); + userMan.AddUser(agentID, url + ";" + first + " " + last); + + try // our best + { + string[] parts = userMan.GetUserName(agentID).Split(); + first = parts[0]; + last = parts[1]; + } + catch { } + return true; + } + return false; + } + + 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); + } + + protected override string FriendshipMessage(string friendID) + { + UUID id; + if (UUID.TryParse(friendID, out id)) + return base.FriendshipMessage(friendID); + + return "Please confirm this friendship you made while you were away."; + } + + protected override void StoreBackwards(UUID friendID, UUID agentID) + { + UserAccount account1 = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, agentID); + UserAccount account2 = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, friendID); + // Are they both local users? + if (account1 != null && account2 != null) + { + // local grid users + m_log.DebugFormat("[HGFRIENDS MODULE]: Users are both local"); + base.StoreBackwards(friendID, agentID); + return; + } + + // no provision for this temporary friendship state + //FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0); + } + + protected override void StoreFriendships(UUID agentID, UUID friendID) + { + UserAccount agentAccount = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, agentID); + UserAccount friendAccount = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, friendID); + // Are they both local users? + if (agentAccount != null && friendAccount != null) + { + // local grid users + m_log.DebugFormat("[HGFRIENDS MODULE]: Users are both local"); + base.StoreFriendships(agentID, friendID); + return; + } + + + // ok, at least one of them is foreigner, let's get their data + IClientAPI agentClient = LocateClientObject(agentID); + IClientAPI friendClient = LocateClientObject(friendID); + AgentCircuitData agentClientCircuit = null; + AgentCircuitData friendClientCircuit = null; + string agentUUI = string.Empty; + string friendUUI = string.Empty; + string agentFriendService = string.Empty; + string friendFriendService = string.Empty; + + if (agentClient != null) + { + agentClientCircuit = ((Scene)(agentClient.Scene)).AuthenticateHandler.GetAgentCircuitData(agentClient.CircuitCode); + agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit); + agentFriendService = agentClientCircuit.ServiceURLs["FriendsServerURI"].ToString(); + } + if (friendClient != null) + { + friendClientCircuit = ((Scene)(friendClient.Scene)).AuthenticateHandler.GetAgentCircuitData(friendClient.CircuitCode); + friendUUI = Util.ProduceUserUniversalIdentifier(friendClientCircuit); + friendFriendService = friendClientCircuit.ServiceURLs["FriendsServerURI"].ToString(); + } + + m_log.DebugFormat("[XXX] HG Friendship! thisUUI={0}; friendUUI={1}; foreignThisFriendService={2}; foreignFriendFriendService={3}", + agentUUI, friendUUI, agentFriendService, friendFriendService); + + if (agentAccount != null) // 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 llok for its information in the friends list itself + if (friendUUI == string.Empty) + { + FriendInfo[] finfos = GetFriends(agentID); + foreach (FriendInfo finfo in finfos) + { + if (finfo.TheirFlags == -1) + { + if (finfo.Friend.StartsWith(friendID.ToString())) + friendUUI = finfo.Friend; + } + } + } + + // store in the local friends service a reference to the foreign friend + FriendsService.StoreFriend(agentID.ToString(), friendUUI, 1); + // and also the converse + FriendsService.StoreFriend(friendUUI, agentID.ToString(), 1); + + if (friendClientCircuit != null) + { + // 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); + } + } + else if (friendAccount != null) // 'friend' is local, agent is foreigner + { + // store in the local friends service a reference to the foreign agent + FriendsService.StoreFriend(friendID.ToString(), agentUUI, 1); + // and also the converse + FriendsService.StoreFriend(agentUUI, friendID.ToString(), 1); + + if (agentClientCircuit != null) + { + // store in the foreign friends service a reference to the local agent + HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(agentFriendService, agentClientCircuit.SessionID, agentClientCircuit.ServiceSessionID); + friendsConn.NewFriendship(agentID, friendUUI); + } + } + else // They're both foreigners! + { + HGFriendsServicesConnector friendsConn; + if (agentClientCircuit != null) + { + friendsConn = new HGFriendsServicesConnector(agentFriendService, agentClientCircuit.SessionID, agentClientCircuit.ServiceSessionID); + friendsConn.NewFriendship(agentID, friendUUI); + } + if (friendClientCircuit != null) + { + friendsConn = new HGFriendsServicesConnector(friendFriendService, friendClientCircuit.SessionID, friendClientCircuit.ServiceSessionID); + friendsConn.NewFriendship(friendID, agentUUI); + } + } + // my brain hurts now + } + + protected override void DeleteFriendship(UUID agentID, UUID exfriendID) + { + base.DeleteFriendship(agentID, exfriendID); + // Maybe some of the base deletes will fail. + // Let's delete the local friendship with foreign friend + FriendInfo[] friends = GetFriends(agentID); + foreach (FriendInfo finfo in friends) + { + if (finfo.Friend != exfriendID.ToString() && finfo.Friend.EndsWith(exfriendID.ToString())) + { + FriendsService.Delete(agentID, exfriendID.ToString()); + // TODO: delete the friendship on the other side + // Should use the homeurl given in finfo.Friend + } + } + } + + //private void OnGrantUserRights(IClientAPI remoteClient, UUID requester, UUID target, int rights) + //{ + // FriendInfo[] friends = GetFriends(remoteClient.AgentId); + // if (friends.Length == 0) + // return; + + // m_log.DebugFormat("[FRIENDS MODULE]: User {0} changing rights to {1} for friend {2}", requester, rights, target); + // // Let's find the friend in this user's friend list + // FriendInfo friend = null; + // foreach (FriendInfo fi in friends) + // { + // if (fi.Friend == target.ToString()) + // friend = fi; + // } + + // if (friend != null) // Found it + // { + // // Store it on the DB + // FriendsService.StoreFriend(requester, target.ToString(), rights); + + // // Store it in the local cache + // int myFlags = friend.MyFlags; + // friend.MyFlags = rights; + + // // Always send this back to the original client + // remoteClient.SendChangeUserRights(requester, target, rights); + + // // + // // Notify the friend + // // + + // // Try local + // if (LocalGrantRights(requester, target, myFlags, rights)) + // return; + + // PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { target.ToString() }); + // if (friendSessions != null && friendSessions.Length > 0) + // { + // PresenceInfo friendSession = friendSessions[0]; + // if (friendSession != null) + // { + // GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + // // TODO: You might want to send the delta to save the lookup + // // on the other end!! + // m_FriendsSimConnector.GrantRights(region, requester, target, myFlags, rights); + // } + // } + // } + //} + + + } +} diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index 4cc6905a0f..795de098b2 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs @@ -141,6 +141,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement void HandleUUIDNameRequest(UUID uuid, IClientAPI remote_client) { + m_log.DebugFormat("[XXX] HandleUUIDNameRequest {0}", uuid); if (m_Scenes[0].LibraryService != null && (m_Scenes[0].LibraryService.LibraryRootFolder.Owner == uuid)) { remote_client.SendNameReply(uuid, "Mr", "OpenSim"); @@ -210,6 +211,7 @@ 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) { diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs index d2343c994e..f5b6817638 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs @@ -115,6 +115,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid new UserAgentServerConnector(m_Config, MainServer.Instance); new HeloServiceInConnector(m_Config, MainServer.Instance, "HeloService"); + new HGFriendsServerConnector(m_Config, MainServer.Instance, "HGFriendsService"); } scene.RegisterModuleInterface(m_HypergridHandler.GateKeeper); } diff --git a/OpenSim/Server/Handlers/Friends/FriendServerConnector.cs b/OpenSim/Server/Handlers/Friends/FriendServerConnector.cs index 074f8690a5..5784bdf652 100644 --- a/OpenSim/Server/Handlers/Friends/FriendServerConnector.cs +++ b/OpenSim/Server/Handlers/Friends/FriendServerConnector.cs @@ -46,14 +46,14 @@ namespace OpenSim.Server.Handlers.Friends if (serverConfig == null) throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); - string gridService = serverConfig.GetString("LocalServiceModule", + string theService = serverConfig.GetString("LocalServiceModule", String.Empty); - if (gridService == String.Empty) + if (theService == String.Empty) throw new Exception("No LocalServiceModule in config file"); Object[] args = new Object[] { config }; - m_FriendsService = ServerUtils.LoadPlugin(gridService, args); + m_FriendsService = ServerUtils.LoadPlugin(theService, args); server.AddStreamHandler(new FriendsServerPostHandler(m_FriendsService)); } diff --git a/OpenSim/Server/Handlers/Friends/FriendsServerPostHandler.cs b/OpenSim/Server/Handlers/Friends/FriendsServerPostHandler.cs index b168bb35c9..64002e1ada 100644 --- a/OpenSim/Server/Handlers/Friends/FriendsServerPostHandler.cs +++ b/OpenSim/Server/Handlers/Friends/FriendsServerPostHandler.cs @@ -138,7 +138,7 @@ namespace OpenSim.Server.Handlers.Friends { FriendInfo friend = new FriendInfo(request); - bool success = m_FriendsService.StoreFriend(friend.PrincipalID, friend.Friend, friend.MyFlags); + bool success = m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, friend.MyFlags); if (success) return SuccessResult(); diff --git a/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs new file mode 100644 index 0000000000..82a72206d3 --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs @@ -0,0 +1,71 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class HGFriendsServerConnector : ServiceConnector + { + private IFriendsService m_FriendsService; + private IUserAgentService m_UserAgentService; + private string m_ConfigName = "HGFriendsService"; + + public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != string.Empty) + m_ConfigName = configName; + + 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); + + theService = serverConfig.GetString("UserAgentService", string.Empty); + if (theService == String.Empty) + throw new Exception("No UserAgentService in " + m_ConfigName); + + m_UserAgentService = ServerUtils.LoadPlugin(theService, args); + + server.AddStreamHandler(new HGFriendsServerPostHandler(m_FriendsService, m_UserAgentService)); + } + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs b/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs new file mode 100644 index 0000000000..13d150265a --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs @@ -0,0 +1,242 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class HGFriendsServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IFriendsService m_FriendsService; + private IUserAgentService m_UserAgentService; + + public HGFriendsServerPostHandler(IFriendsService service, IUserAgentService uservice) : + base("POST", "/hgfriends") + { + m_FriendsService = service; + m_UserAgentService = uservice; + m_log.DebugFormat("[HGFRIENDS HANDLER]: HGFriendsServerPostHandler is On"); + } + + public override byte[] Handle(string path, Stream requestData, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + + switch (method) + { + case "getfriends": + return GetFriends(request); + + case "newfriendship": + return NewFriendship(request); + + } + m_log.DebugFormat("[HGFRIENDS HANDLER]: unknown method {0} request {1}", method.Length, method); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS HANDLER]: Exception {0}", e); + } + + return FailureResult(); + + } + + #region Method-specific handlers + + byte[] GetFriends(Dictionary request) + { + UUID principalID = UUID.Zero; + if (request.ContainsKey("PRINCIPALID")) + UUID.TryParse(request["PRINCIPALID"].ToString(), out principalID); + else + m_log.WarnFormat("[HGFRIENDS HANDLER]: no principalID in request to get friends"); + + FriendInfo[] finfos = m_FriendsService.GetFriends(principalID); + //m_log.DebugFormat("[FRIENDS HANDLER]: neighbours for region {0}: {1}", regionID, rinfos.Count); + + Dictionary result = new Dictionary(); + if ((finfos == null) || ((finfos != null) && (finfos.Length == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (FriendInfo finfo in finfos) + { + Dictionary rinfoDict = finfo.ToKeyValuePairs(); + result["friend" + i] = rinfoDict; + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + //m_log.DebugFormat("[FRIENDS HANDLER]: resp string: {0}", xmlString); + UTF8Encoding encoding = new UTF8Encoding(); + return encoding.GetBytes(xmlString); + + } + + byte[] NewFriendship(Dictionary request) + { + if (!request.ContainsKey("KEY") || !request.ContainsKey("SESSIONID")) + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: ignoring request without Key or SessionID"); + return FailureResult(); + } + + string serviceKey = request["KEY"].ToString(); + string sessionStr = request["SESSIONID"].ToString(); + UUID sessionID; + UUID.TryParse(sessionStr, out sessionID); + + if (!m_UserAgentService.VerifyAgent(sessionID, serviceKey)) + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: Key {0} for session {1} did not match existing key. Ignoring request", serviceKey, sessionID); + return FailureResult(); + } + + m_log.DebugFormat("[XXX] Verification ok"); + // OK, can proceed + FriendInfo friend = new FriendInfo(request); + + // the user needs to confirm when he gets home + bool success = m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 0); + //if (success) + // m_FriendsService.StoreFriend(friend.Friend, friend.PrincipalID.ToString(), 1); + + if (success) + return SuccessResult(); + else + return FailureResult(); + } + + #endregion + + #region Misc + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + + private byte[] FailureResult() + { + return FailureResult(String.Empty); + } + + private byte[] FailureResult(string msg) + { + 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("Failure")); + + rootElement.AppendChild(result); + + XmlElement message = doc.CreateElement("", "Message", ""); + message.AppendChild(doc.CreateTextNode(msg)); + + rootElement.AppendChild(message); + + return DocToBytes(doc); + } + + private byte[] DocToBytes(XmlDocument doc) + { + MemoryStream ms = new MemoryStream(); + XmlTextWriter xw = new XmlTextWriter(ms, null); + xw.Formatting = Formatting.Indented; + doc.WriteTo(xw); + xw.Flush(); + + return ms.ToArray(); + } + + #endregion + } +} diff --git a/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs index 861c4759a3..52b80e1ffd 100644 --- a/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs +++ b/OpenSim/Services/Connectors/Friends/FriendsServiceConnector.cs @@ -38,7 +38,7 @@ using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; using OpenSim.Server.Base; using OpenMetaverse; -namespace OpenSim.Services.Connectors +namespace OpenSim.Services.Connectors.Friends { public class FriendsServicesConnector : IFriendsService { @@ -144,10 +144,17 @@ namespace OpenSim.Services.Connectors } - public bool StoreFriend(UUID PrincipalID, string Friend, int flags) + public bool StoreFriend(string PrincipalID, string Friend, int flags) { FriendInfo finfo = new FriendInfo(); - finfo.PrincipalID = PrincipalID; + try + { + finfo.PrincipalID = new UUID(PrincipalID); + } + catch + { + return false; + } finfo.Friend = Friend; finfo.MyFlags = flags; diff --git a/OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs new file mode 100644 index 0000000000..76f5f190a9 --- /dev/null +++ b/OpenSim/Services/Connectors/Hypergrid/HGFriendsServiceConnector.cs @@ -0,0 +1,175 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.Friends; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors.Hypergrid +{ + public class HGFriendsServicesConnector + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + private string m_ServiceKey = String.Empty; + private UUID m_SessionID; + + public HGFriendsServicesConnector() + { + } + + public HGFriendsServicesConnector(string serverURI, UUID sessionID, string serviceKey) + { + m_ServerURI = serverURI.TrimEnd('/'); + m_ServiceKey = serviceKey; + m_SessionID = sessionID; + } + + #region IFriendsService + + public FriendInfo[] GetFriends(UUID PrincipalID) + { + Dictionary sendData = new Dictionary(); + + sendData["PRINCIPALID"] = PrincipalID.ToString(); + sendData["METHOD"] = "getfriends"; + sendData["KEY"] = m_ServiceKey; + sendData["SESSIONID"] = m_SessionID.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/hgfriends", + reqString); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + if (replyData.ContainsKey("result") && (replyData["result"].ToString().ToLower() == "null")) + { + return new FriendInfo[0]; + } + + List finfos = new List(); + Dictionary.ValueCollection finfosList = replyData.Values; + //m_log.DebugFormat("[FRIENDS CONNECTOR]: get neighbours returned {0} elements", rinfosList.Count); + foreach (object f in finfosList) + { + if (f is Dictionary) + { + FriendInfo finfo = new FriendInfo((Dictionary)f); + finfos.Add(finfo); + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: GetFriends {0} received invalid response type {1}", + PrincipalID, f.GetType()); + } + + // Success + return finfos.ToArray(); + } + + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: GetFriends {0} received null response", + PrincipalID); + + } + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server: {0}", e.Message); + } + + return new FriendInfo[0]; + + } + + public bool NewFriendship(UUID PrincipalID, string Friend) + { + FriendInfo finfo = new FriendInfo(); + finfo.PrincipalID = PrincipalID; + finfo.Friend = Friend; + + Dictionary sendData = finfo.ToKeyValuePairs(); + + sendData["METHOD"] = "newfriendship"; + sendData["KEY"] = m_ServiceKey; + sendData["SESSIONID"] = m_SessionID.ToString(); + + string reply = string.Empty; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/hgfriends", + ServerUtils.BuildQueryString(sendData)); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server: {0}", e.Message); + return false; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && replyData.ContainsKey("Result") && (replyData["Result"] != null)) + { + bool success = false; + Boolean.TryParse(replyData["Result"].ToString(), out success); + return success; + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: StoreFriend {0} {1} received null response", + PrincipalID, Friend); + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: StoreFriend received null reply"); + + return false; + + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs index 6f2d73559b..f61ab2956d 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs @@ -127,7 +127,7 @@ namespace OpenSim.Services.Connectors.SimianGrid return array; } - public bool StoreFriend(UUID principalID, string friend, int flags) + public bool StoreFriend(string principalID, string friend, int flags) { if (String.IsNullOrEmpty(m_serverUrl)) return true; diff --git a/OpenSim/Services/Friends/FriendsService.cs b/OpenSim/Services/Friends/FriendsService.cs index 3c64eccdf6..039dc0bceb 100644 --- a/OpenSim/Services/Friends/FriendsService.cs +++ b/OpenSim/Services/Friends/FriendsService.cs @@ -43,17 +43,16 @@ namespace OpenSim.Services.Friends { } - public FriendInfo[] GetFriends(UUID PrincipalID) + public virtual FriendInfo[] GetFriends(UUID PrincipalID) { FriendsData[] data = m_Database.GetFriends(PrincipalID); - List info = new List(); foreach (FriendsData d in data) { FriendInfo i = new FriendInfo(); - i.PrincipalID = d.PrincipalID; + i.PrincipalID = new UUID(d.PrincipalID); i.Friend = d.Friend; i.MyFlags = Convert.ToInt32(d.Data["Flags"]); i.TheirFlags = Convert.ToInt32(d.Data["TheirFlags"]); @@ -64,7 +63,7 @@ namespace OpenSim.Services.Friends return info.ToArray(); } - public bool StoreFriend(UUID PrincipalID, string Friend, int flags) + public virtual bool StoreFriend(string PrincipalID, string Friend, int flags) { FriendsData d = new FriendsData(); @@ -76,7 +75,7 @@ namespace OpenSim.Services.Friends return m_Database.Store(d); } - public bool Delete(UUID PrincipalID, string Friend) + public virtual bool Delete(UUID PrincipalID, string Friend) { return m_Database.Delete(PrincipalID, Friend); } diff --git a/OpenSim/Services/HypergridService/HGFriendsService.cs b/OpenSim/Services/HypergridService/HGFriendsService.cs new file mode 100644 index 0000000000..fa4ec5d7ec --- /dev/null +++ b/OpenSim/Services/HypergridService/HGFriendsService.cs @@ -0,0 +1,95 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using System; +using System.Collections.Generic; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Friends; +using OpenSim.Data; +using Nini.Config; +using log4net; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +namespace OpenSim.Services.HypergridService +{ + public class HGFriendsService : FriendsService, IFriendsService + { + public HGFriendsService(IConfigSource config) : base(config) + { + } + + /// + /// Overrides base. + /// Storing new friendships from the outside is a tricky, sensitive operation, and it + /// needs to be done under certain restrictions. + /// First of all, if the friendship already exists, this is a no-op. In other words, + /// we cannot change just the flags, it needs to be a new friendship. + /// Second, we store it as flags=0 always, independent of what the caller sends. The + /// owner of the friendship needs to confirm when it gets back home. + /// + /// + /// + /// + /// + public override bool StoreFriend(string PrincipalID, string Friend, int flags) + { + UUID userID; + if (UUID.TryParse(PrincipalID, out userID)) + { + FriendsData[] friendsData = m_Database.GetFriends(userID); + List fList = new List(friendsData); + if (fList.Find(delegate(FriendsData fdata) + { + return fdata.Friend == Friend; + }) != null) + return false; + } + + FriendsData d = new FriendsData(); + d.PrincipalID = PrincipalID; + d.Friend = Friend; + d.Data = new Dictionary(); + d.Data["Flags"] = "0"; + + return m_Database.Store(d); + } + + /// + /// Overrides base. Cannot delete friendships while away from home. + /// + /// + /// + /// + public override bool Delete(UUID PrincipalID, string Friend) + { + return false; + } + + } +} diff --git a/OpenSim/Services/Interfaces/IFriendsService.cs b/OpenSim/Services/Interfaces/IFriendsService.cs index 0ddd5e53a9..05e85f2318 100644 --- a/OpenSim/Services/Interfaces/IFriendsService.cs +++ b/OpenSim/Services/Interfaces/IFriendsService.cs @@ -74,7 +74,7 @@ namespace OpenSim.Services.Interfaces public interface IFriendsService { FriendInfo[] GetFriends(UUID PrincipalID); - bool StoreFriend(UUID PrincipalID, string Friend, int flags); + bool StoreFriend(string PrincipalID, string Friend, int flags); bool Delete(UUID PrincipalID, string Friend); } } diff --git a/OpenSim/Services/LLLoginService/LLLoginResponse.cs b/OpenSim/Services/LLLoginService/LLLoginResponse.cs index ddc8855145..4fac951659 100644 --- a/OpenSim/Services/LLLoginService/LLLoginResponse.cs +++ b/OpenSim/Services/LLLoginService/LLLoginResponse.cs @@ -621,7 +621,19 @@ namespace OpenSim.Services.LLLoginService if (finfo.TheirFlags == -1) continue; LLLoginResponse.BuddyList.BuddyInfo buddyitem = new LLLoginResponse.BuddyList.BuddyInfo(finfo.Friend); - buddyitem.BuddyID = finfo.Friend; + // finfo.Friend may not be a simple uuid + UUID friendID = UUID.Zero; + if (UUID.TryParse(finfo.Friend, out friendID)) + buddyitem.BuddyID = finfo.Friend; + else + { + string tmp; + if (Util.ParseUniversalUserIdentifier(finfo.Friend, out friendID, out tmp, out tmp, out tmp)) + buddyitem.BuddyID = friendID.ToString(); + else + // junk entry + continue; + } buddyitem.BuddyRightsHave = (int)finfo.TheirFlags; buddyitem.BuddyRightsGiven = (int)finfo.MyFlags; buddylistreturn.AddNewBuddy(buddyitem); diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini index 486f22e698..324c258c30 100644 --- a/bin/config-include/StandaloneHypergrid.ini +++ b/bin/config-include/StandaloneHypergrid.ini @@ -20,6 +20,7 @@ AvatarServices = "LocalAvatarServicesConnector" EntityTransferModule = "HGEntityTransferModule" InventoryAccessModule = "HGInventoryAccessModule" + FriendsModule = "HGFriendsModule" InventoryServiceInConnector = true AssetServiceInConnector = true @@ -140,6 +141,10 @@ LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGAssetService" UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" +[HGFriendsService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGFriendsService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + ;; This should always be the very last thing on this file [Includes] Include-Common = "config-include/StandaloneCommon.ini" diff --git a/prebuild.xml b/prebuild.xml index a07ad14154..1f620dc8b1 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -1256,6 +1256,7 @@ +