From 9380d01976726885bd993573aa649f2cb0992909 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 19 Feb 2013 07:26:40 -0800 Subject: First commit of Diva Groups. The Data bits went to OpenSim.Data core, the rest to Addons.Groups.dll. --- OpenSim/Addons/Groups/ForeignImporter.cs | 77 + OpenSim/Addons/Groups/GroupsExtendedData.cs | 509 +++++++ OpenSim/Addons/Groups/GroupsMessagingModule.cs | 594 ++++++++ OpenSim/Addons/Groups/GroupsModule.cs | 1467 ++++++++++++++++++++ .../Groups/Hypergrid/GroupsServiceHGConnector.cs | 289 ++++ .../Hypergrid/GroupsServiceHGConnectorModule.cs | 717 ++++++++++ .../Hypergrid/HGGroupsServiceRobustConnector.cs | 443 ++++++ OpenSim/Addons/Groups/IGroupsServicesConnector.cs | 118 ++ .../Local/GroupsServiceLocalConnectorModule.cs | 347 +++++ OpenSim/Addons/Groups/Properties/AssemblyInfo.cs | 37 + .../Groups/Remote/GroupsServiceRemoteConnector.cs | 642 +++++++++ .../Remote/GroupsServiceRemoteConnectorModule.cs | 437 ++++++ .../Groups/Remote/GroupsServiceRobustConnector.cs | 760 ++++++++++ .../Addons/Groups/RemoteConnectorCacheWrapper.cs | 824 +++++++++++ OpenSim/Addons/Groups/Service/GroupsService.cs | 1014 ++++++++++++++ OpenSim/Addons/Groups/Service/GroupsServiceBase.cs | 84 ++ OpenSim/Addons/Groups/Service/HGGroupsService.cs | 353 +++++ 17 files changed, 8712 insertions(+) create mode 100644 OpenSim/Addons/Groups/ForeignImporter.cs create mode 100644 OpenSim/Addons/Groups/GroupsExtendedData.cs create mode 100644 OpenSim/Addons/Groups/GroupsMessagingModule.cs create mode 100644 OpenSim/Addons/Groups/GroupsModule.cs create mode 100644 OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs create mode 100644 OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs create mode 100644 OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs create mode 100644 OpenSim/Addons/Groups/IGroupsServicesConnector.cs create mode 100644 OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs create mode 100644 OpenSim/Addons/Groups/Properties/AssemblyInfo.cs create mode 100644 OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs create mode 100644 OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs create mode 100644 OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs create mode 100644 OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs create mode 100644 OpenSim/Addons/Groups/Service/GroupsService.cs create mode 100644 OpenSim/Addons/Groups/Service/GroupsServiceBase.cs create mode 100644 OpenSim/Addons/Groups/Service/HGGroupsService.cs (limited to 'OpenSim/Addons/Groups') diff --git a/OpenSim/Addons/Groups/ForeignImporter.cs b/OpenSim/Addons/Groups/ForeignImporter.cs new file mode 100644 index 0000000..788d21d --- /dev/null +++ b/OpenSim/Addons/Groups/ForeignImporter.cs @@ -0,0 +1,77 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Groups +{ + public class ForeignImporter + { + IUserManagement m_UserManagement; + public ForeignImporter(IUserManagement uman) + { + m_UserManagement = uman; + } + + public GroupMembersData ConvertGroupMembersData(ExtendedGroupMembersData _m) + { + GroupMembersData m = new GroupMembersData(); + m.AcceptNotices = _m.AcceptNotices; + m.AgentPowers = _m.AgentPowers; + m.Contribution = _m.Contribution; + m.IsOwner = _m.IsOwner; + m.ListInProfile = _m.ListInProfile; + m.OnlineStatus = _m.OnlineStatus; + m.Title = _m.Title; + + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + Util.ParseUniversalUserIdentifier(_m.AgentID, out m.AgentID, out url, out first, out last, out tmp); + if (url != string.Empty) + m_UserManagement.AddUser(m.AgentID, first, last, url); + + return m; + } + + public GroupRoleMembersData ConvertGroupRoleMembersData(ExtendedGroupRoleMembersData _rm) + { + GroupRoleMembersData rm = new GroupRoleMembersData(); + rm.RoleID = _rm.RoleID; + + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + Util.ParseUniversalUserIdentifier(_rm.MemberID, out rm.MemberID, out url, out first, out last, out tmp); + if (url != string.Empty) + m_UserManagement.AddUser(rm.MemberID, first, last, url); + + return rm; + } + + } +} diff --git a/OpenSim/Addons/Groups/GroupsExtendedData.cs b/OpenSim/Addons/Groups/GroupsExtendedData.cs new file mode 100644 index 0000000..6f4db28 --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsExtendedData.cs @@ -0,0 +1,509 @@ +/* + * 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public class ExtendedGroupRecord : GroupRecord + { + public int MemberCount; + public int RoleCount; + public string ServiceLocation; + public string FounderUUI; + } + + public class ExtendedGroupMembershipData : GroupMembershipData + { + public string AccessToken; + } + + public class ExtendedGroupMembersData + { + // This is the only difference: this is a string + public string AgentID; + public int Contribution; + public string OnlineStatus; + public ulong AgentPowers; + public string Title; + public bool IsOwner; + public bool ListInProfile; + public bool AcceptNotices; + public string AccessToken; + } + + public class ExtendedGroupRoleMembersData + { + public UUID RoleID; + // This is the only difference: this is a string + public string MemberID; + + } + + public struct ExtendedGroupNoticeData + { + public UUID NoticeID; + public uint Timestamp; + public string FromName; + public string Subject; + public bool HasAttachment; + public byte AttachmentType; + public string AttachmentName; + public UUID AttachmentItemID; + public string AttachmentOwnerID; + + public GroupNoticeData ToGroupNoticeData() + { + GroupNoticeData n = new GroupNoticeData(); + n.FromName = this.FromName; + n.AssetType = this.AttachmentType; + n.HasAttachment = this.HasAttachment; + n.NoticeID = this.NoticeID; + n.Subject = this.Subject; + n.Timestamp = this.Timestamp; + + return n; + } + } + + public class GroupsDataUtils + { + public static string Sanitize(string s) + { + return s == null ? string.Empty : s; + } + + public static Dictionary GroupRecord(ExtendedGroupRecord grec) + { + Dictionary dict = new Dictionary(); + if (grec == null) + return dict; + + dict["AllowPublish"] = grec.AllowPublish.ToString(); + dict["Charter"] = Sanitize(grec.Charter); + dict["FounderID"] = grec.FounderID.ToString(); + dict["FounderUUI"] = Sanitize(grec.FounderUUI); + dict["GroupID"] = grec.GroupID.ToString(); + dict["GroupName"] = Sanitize(grec.GroupName); + dict["InsigniaID"] = grec.GroupPicture.ToString(); + dict["MaturePublish"] = grec.MaturePublish.ToString(); + dict["MembershipFee"] = grec.MembershipFee.ToString(); + dict["OpenEnrollment"] = grec.OpenEnrollment.ToString(); + dict["OwnerRoleID"] = grec.OwnerRoleID.ToString(); + dict["ServiceLocation"] = Sanitize(grec.ServiceLocation); + dict["ShownInList"] = grec.ShowInList.ToString(); + dict["MemberCount"] = grec.MemberCount.ToString(); + dict["RoleCount"] = grec.RoleCount.ToString(); + + return dict; + } + + public static ExtendedGroupRecord GroupRecord(Dictionary dict) + { + if (dict == null) + return null; + + ExtendedGroupRecord grec = new ExtendedGroupRecord(); + if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null) + grec.AllowPublish = bool.Parse(dict["AllowPublish"].ToString()); + + if (dict.ContainsKey("Charter") && dict["Charter"] != null) + grec.Charter = dict["Charter"].ToString(); + else + grec.Charter = string.Empty; + + if (dict.ContainsKey("FounderID") && dict["FounderID"] != null) + grec.FounderID = UUID.Parse(dict["FounderID"].ToString()); + + if (dict.ContainsKey("FounderUUI") && dict["FounderUUI"] != null) + grec.FounderUUI = dict["FounderUUI"].ToString(); + else + grec.FounderUUI = string.Empty; + + if (dict.ContainsKey("GroupID") && dict["GroupID"] != null) + grec.GroupID = UUID.Parse(dict["GroupID"].ToString()); + + if (dict.ContainsKey("GroupName") && dict["GroupName"] != null) + grec.GroupName = dict["GroupName"].ToString(); + else + grec.GroupName = string.Empty; + + if (dict.ContainsKey("InsigniaID") && dict["InsigniaID"] != null) + grec.GroupPicture = UUID.Parse(dict["InsigniaID"].ToString()); + + if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null) + grec.MaturePublish = bool.Parse(dict["MaturePublish"].ToString()); + + if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null) + grec.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString()); + + if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null) + grec.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString()); + + if (dict.ContainsKey("OwnerRoleID") && dict["OwnerRoleID"] != null) + grec.OwnerRoleID = UUID.Parse(dict["OwnerRoleID"].ToString()); + + if (dict.ContainsKey("ServiceLocation") && dict["ServiceLocation"] != null) + grec.ServiceLocation = dict["ServiceLocation"].ToString(); + else + grec.GroupName = string.Empty; + + if (dict.ContainsKey("ShownInList") && dict["ShownInList"] != null) + grec.ShowInList = bool.Parse(dict["ShownInList"].ToString()); + + if (dict.ContainsKey("MemberCount") && dict["MemberCount"] != null) + grec.MemberCount = Int32.Parse(dict["MemberCount"].ToString()); + + if (dict.ContainsKey("RoleCount") && dict["RoleCount"] != null) + grec.RoleCount = Int32.Parse(dict["RoleCount"].ToString()); + + return grec; + } + + public static Dictionary GroupMembershipData(ExtendedGroupMembershipData membership) + { + Dictionary dict = new Dictionary(); + if (membership == null) + return dict; + + dict["AcceptNotices"] = membership.AcceptNotices.ToString(); + dict["AccessToken"] = Sanitize(membership.AccessToken); + dict["Active"] = membership.Active.ToString(); + dict["ActiveRole"] = membership.ActiveRole.ToString(); + dict["AllowPublish"] = membership.AllowPublish.ToString(); + dict["Charter"] = Sanitize(membership.Charter); + dict["Contribution"] = membership.Contribution.ToString(); + dict["FounderID"] = membership.FounderID.ToString(); + dict["GroupID"] = membership.GroupID.ToString(); + dict["GroupName"] = Sanitize(membership.GroupName); + dict["GroupPicture"] = membership.GroupPicture.ToString(); + dict["GroupPowers"] = membership.GroupPowers.ToString(); + dict["GroupTitle"] = Sanitize(membership.GroupTitle); + dict["ListInProfile"] = membership.ListInProfile.ToString(); + dict["MaturePublish"] = membership.MaturePublish.ToString(); + dict["MembershipFee"] = membership.MembershipFee.ToString(); + dict["OpenEnrollment"] = membership.OpenEnrollment.ToString(); + dict["ShowInList"] = membership.ShowInList.ToString(); + + return dict; + } + + public static ExtendedGroupMembershipData GroupMembershipData(Dictionary dict) + { + if (dict == null) + return null; + + ExtendedGroupMembershipData membership = new ExtendedGroupMembershipData(); + + if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null) + membership.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString()); + + if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null) + membership.AccessToken = dict["AccessToken"].ToString(); + else + membership.AccessToken = string.Empty; + + if (dict.ContainsKey("Active") && dict["Active"] != null) + membership.Active = bool.Parse(dict["Active"].ToString()); + + if (dict.ContainsKey("ActiveRole") && dict["ActiveRole"] != null) + membership.ActiveRole = UUID.Parse(dict["ActiveRole"].ToString()); + + if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null) + membership.AllowPublish = bool.Parse(dict["AllowPublish"].ToString()); + + if (dict.ContainsKey("Charter") && dict["Charter"] != null) + membership.Charter = dict["Charter"].ToString(); + else + membership.Charter = string.Empty; + + if (dict.ContainsKey("Contribution") && dict["Contribution"] != null) + membership.Contribution = Int32.Parse(dict["Contribution"].ToString()); + + if (dict.ContainsKey("FounderID") && dict["FounderID"] != null) + membership.FounderID = UUID.Parse(dict["FounderID"].ToString()); + + if (dict.ContainsKey("GroupID") && dict["GroupID"] != null) + membership.GroupID = UUID.Parse(dict["GroupID"].ToString()); + + if (dict.ContainsKey("GroupName") && dict["GroupName"] != null) + membership.GroupName = dict["GroupName"].ToString(); + else + membership.GroupName = string.Empty; + + if (dict.ContainsKey("GroupPicture") && dict["GroupPicture"] != null) + membership.GroupPicture = UUID.Parse(dict["GroupPicture"].ToString()); + + if (dict.ContainsKey("GroupPowers") && dict["GroupPowers"] != null) + membership.GroupPowers = UInt64.Parse(dict["GroupPowers"].ToString()); + + if (dict.ContainsKey("GroupTitle") && dict["GroupTitle"] != null) + membership.GroupTitle = dict["GroupTitle"].ToString(); + else + membership.GroupTitle = string.Empty; + + if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null) + membership.ListInProfile = bool.Parse(dict["ListInProfile"].ToString()); + + if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null) + membership.MaturePublish = bool.Parse(dict["MaturePublish"].ToString()); + + if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null) + membership.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString()); + + if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null) + membership.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString()); + + if (dict.ContainsKey("ShowInList") && dict["ShowInList"] != null) + membership.ShowInList = bool.Parse(dict["ShowInList"].ToString()); + + return membership; + } + + public static Dictionary GroupMembersData(ExtendedGroupMembersData member) + { + Dictionary dict = new Dictionary(); + + dict["AcceptNotices"] = member.AcceptNotices.ToString(); + dict["AccessToken"] = Sanitize(member.AccessToken); + dict["AgentID"] = Sanitize(member.AgentID); + dict["AgentPowers"] = member.AgentPowers.ToString(); + dict["Contribution"] = member.Contribution.ToString(); + dict["IsOwner"] = member.IsOwner.ToString(); + dict["ListInProfile"] = member.ListInProfile.ToString(); + dict["OnlineStatus"] = Sanitize(member.OnlineStatus); + dict["Title"] = Sanitize(member.Title); + + return dict; + } + + public static ExtendedGroupMembersData GroupMembersData(Dictionary dict) + { + ExtendedGroupMembersData member = new ExtendedGroupMembersData(); + + if (dict == null) + return member; + + if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null) + member.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString()); + + if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null) + member.AccessToken = Sanitize(dict["AccessToken"].ToString()); + else + member.AccessToken = string.Empty; + + if (dict.ContainsKey("AgentID") && dict["AgentID"] != null) + member.AgentID = Sanitize(dict["AgentID"].ToString()); + else + member.AgentID = UUID.Zero.ToString(); + + if (dict.ContainsKey("AgentPowers") && dict["AgentPowers"] != null) + member.AgentPowers = UInt64.Parse(dict["AgentPowers"].ToString()); + + if (dict.ContainsKey("Contribution") && dict["Contribution"] != null) + member.Contribution = Int32.Parse(dict["Contribution"].ToString()); + + if (dict.ContainsKey("IsOwner") && dict["IsOwner"] != null) + member.IsOwner = bool.Parse(dict["IsOwner"].ToString()); + + if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null) + member.ListInProfile = bool.Parse(dict["ListInProfile"].ToString()); + + if (dict.ContainsKey("OnlineStatus") && dict["OnlineStatus"] != null) + member.OnlineStatus = Sanitize(dict["OnlineStatus"].ToString()); + else + member.OnlineStatus = string.Empty; + + if (dict.ContainsKey("Title") && dict["Title"] != null) + member.Title = Sanitize(dict["Title"].ToString()); + else + member.Title = string.Empty; + + return member; + } + + public static Dictionary GroupRolesData(GroupRolesData role) + { + Dictionary dict = new Dictionary(); + + dict["Description"] = Sanitize(role.Description); + dict["Members"] = role.Members.ToString(); + dict["Name"] = Sanitize(role.Name); + dict["Powers"] = role.Powers.ToString(); + dict["RoleID"] = role.RoleID.ToString(); + dict["Title"] = Sanitize(role.Title); + + return dict; + } + + public static GroupRolesData GroupRolesData(Dictionary dict) + { + GroupRolesData role = new GroupRolesData(); + + if (dict == null) + return role; + + if (dict.ContainsKey("Description") && dict["Description"] != null) + role.Description = Sanitize(dict["Description"].ToString()); + else + role.Description = string.Empty; + + if (dict.ContainsKey("Members") && dict["Members"] != null) + role.Members = Int32.Parse(dict["Members"].ToString()); + + if (dict.ContainsKey("Name") && dict["Name"] != null) + role.Name = Sanitize(dict["Name"].ToString()); + else + role.Name = string.Empty; + + if (dict.ContainsKey("Powers") && dict["Powers"] != null) + role.Powers = UInt64.Parse(dict["Powers"].ToString()); + + if (dict.ContainsKey("Title") && dict["Title"] != null) + role.Title = Sanitize(dict["Title"].ToString()); + else + role.Title = string.Empty; + + if (dict.ContainsKey("RoleID") && dict["RoleID"] != null) + role.RoleID = UUID.Parse(dict["RoleID"].ToString()); + + return role; + } + + public static Dictionary GroupRoleMembersData(ExtendedGroupRoleMembersData rmember) + { + Dictionary dict = new Dictionary(); + + dict["RoleID"] = rmember.RoleID.ToString(); + dict["MemberID"] = rmember.MemberID; + return dict; + } + + public static ExtendedGroupRoleMembersData GroupRoleMembersData(Dictionary dict) + { + ExtendedGroupRoleMembersData rmember = new ExtendedGroupRoleMembersData(); + + if (dict.ContainsKey("RoleID") && dict["RoleID"] != null) + rmember.RoleID = new UUID(dict["RoleID"].ToString()); + + if (dict.ContainsKey("MemberID") && dict["MemberID"] != null) + rmember.MemberID = dict["MemberID"].ToString(); + + return rmember; + } + + public static Dictionary GroupInviteInfo(GroupInviteInfo invite) + { + Dictionary dict = new Dictionary(); + + dict["InviteID"] = invite.InviteID.ToString(); + dict["GroupID"] = invite.GroupID.ToString(); + dict["RoleID"] = invite.RoleID.ToString(); + dict["AgentID"] = invite.AgentID; + + return dict; + } + + public static GroupInviteInfo GroupInviteInfo(Dictionary dict) + { + if (dict == null) + return null; + + GroupInviteInfo invite = new GroupInviteInfo(); + + invite.InviteID = new UUID(dict["InviteID"].ToString()); + invite.GroupID = new UUID(dict["GroupID"].ToString()); + invite.RoleID = new UUID(dict["RoleID"].ToString()); + invite.AgentID = Sanitize(dict["AgentID"].ToString()); + + return invite; + } + + public static Dictionary GroupNoticeData(ExtendedGroupNoticeData notice) + { + Dictionary dict = new Dictionary(); + + dict["NoticeID"] = notice.NoticeID.ToString(); + dict["Timestamp"] = notice.Timestamp.ToString(); + dict["FromName"] = Sanitize(notice.FromName); + dict["Subject"] = Sanitize(notice.Subject); + dict["HasAttachment"] = notice.HasAttachment.ToString(); + dict["AttachmentItemID"] = notice.AttachmentItemID.ToString(); + dict["AttachmentName"] = Sanitize(notice.AttachmentName); + dict["AttachmentType"] = notice.AttachmentType.ToString(); + dict["AttachmentOwnerID"] = Sanitize(notice.AttachmentOwnerID); + + return dict; + } + + public static ExtendedGroupNoticeData GroupNoticeData(Dictionary dict) + { + ExtendedGroupNoticeData notice = new ExtendedGroupNoticeData(); + + if (dict == null) + return notice; + + notice.NoticeID = new UUID(dict["NoticeID"].ToString()); + notice.Timestamp = UInt32.Parse(dict["Timestamp"].ToString()); + notice.FromName = Sanitize(dict["FromName"].ToString()); + notice.Subject = Sanitize(dict["Subject"].ToString()); + notice.HasAttachment = bool.Parse(dict["HasAttachment"].ToString()); + notice.AttachmentItemID = new UUID(dict["AttachmentItemID"].ToString()); + notice.AttachmentName = dict["AttachmentName"].ToString(); + notice.AttachmentType = byte.Parse(dict["AttachmentType"].ToString()); + notice.AttachmentOwnerID = dict["AttachmentOwnerID"].ToString(); + + return notice; + } + + public static Dictionary GroupNoticeInfo(GroupNoticeInfo notice) + { + Dictionary dict = GroupNoticeData(notice.noticeData); + + dict["GroupID"] = notice.GroupID.ToString(); + dict["Message"] = Sanitize(notice.Message); + + return dict; + } + + public static GroupNoticeInfo GroupNoticeInfo(Dictionary dict) + { + GroupNoticeInfo notice = new GroupNoticeInfo(); + + notice.noticeData = GroupNoticeData(dict); + notice.GroupID = new UUID(dict["GroupID"].ToString()); + notice.Message = Sanitize(dict["Message"].ToString()); + + return notice; + } + } + +} diff --git a/OpenSim/Addons/Groups/GroupsMessagingModule.cs b/OpenSim/Addons/Groups/GroupsMessagingModule.cs new file mode 100644 index 0000000..d172d48 --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsMessagingModule.cs @@ -0,0 +1,594 @@ +/* + * 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.Linq; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsMessagingModule")] + public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_sceneList = new List(); + private IPresenceService m_presenceService; + + private IMessageTransferModule m_msgTransferModule = null; + + private IGroupsServicesConnector m_groupData = null; + + // Config Options + private bool m_groupMessagingEnabled = false; + private bool m_debugEnabled = true; + + /// + /// If enabled, module only tries to send group IMs to online users by querying cached presence information. + /// + private bool m_messageOnlineAgentsOnly; + + /// + /// Cache for online users. + /// + /// + /// Group ID is key, presence information for online members is value. + /// Will only be non-null if m_messageOnlineAgentsOnly = true + /// We cache here so that group messages don't constantly have to re-request the online user list to avoid + /// attempted expensive sending of messages to offline users. + /// The tradeoff is that a user that comes online will not receive messages consistently from all other users + /// until caches have updated. + /// Therefore, we set the cache expiry to just 20 seconds. + /// + private ExpiringCache m_usersOnlineCache; + + private int m_usersOnlineCacheExpirySeconds = 20; + + #region Region Module interfaceBase Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + if (groupsConfig == null) + // Do not run this module by default. + return; + + // if groups aren't enabled, we're not needed. + // if we're not specified as the connector to use, then we're not wanted + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("MessagingModule", "") != Name)) + { + m_groupMessagingEnabled = false; + return; + } + + m_groupMessagingEnabled = groupsConfig.GetBoolean("MessagingEnabled", true); + + if (!m_groupMessagingEnabled) + return; + + m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false); + + if (m_messageOnlineAgentsOnly) + m_usersOnlineCache = new ExpiringCache(); + + m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true); + + m_log.InfoFormat( + "[Groups.Messaging]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}", + m_messageOnlineAgentsOnly, m_debugEnabled); + } + + public void AddRegion(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + scene.RegisterModuleInterface(this); + m_sceneList.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + scene.EventManager.OnClientLogin += OnClientLogin; + } + + public void RegionLoaded(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData = scene.RequestModuleInterface(); + + // No groups module, no groups messaging + if (m_groupData == null) + { + m_log.Error("[Groups.Messaging]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled."); + RemoveRegion(scene); + return; + } + + m_msgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no groups messaging + if (m_msgTransferModule == null) + { + m_log.Error("[Groups.Messaging]: Could not get MessageTransferModule"); + RemoveRegion(scene); + return; + } + + if (m_presenceService == null) + m_presenceService = scene.PresenceService; + + } + + public void RemoveRegion(Scene scene) + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_sceneList.Remove(scene); + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; + scene.EventManager.OnClientLogin -= OnClientLogin; + scene.UnregisterModuleInterface(this); + } + + public void Close() + { + if (!m_groupMessagingEnabled) + return; + + if (m_debugEnabled) m_log.Debug("[Groups.Messaging]: Shutting down GroupsMessagingModule module."); + + m_sceneList.Clear(); + + m_groupData = null; + m_msgTransferModule = null; + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "Groups Messaging Module V2"; } + } + + public void PostInitialise() + { + // NoOp + } + + #endregion + + + /// + /// Not really needed, but does confirm that the group exists. + /// + public bool StartGroupChatSession(UUID agentID, UUID groupID) + { + if (m_debugEnabled) + m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + + if (groupInfo != null) + { + return true; + } + else + { + return false; + } + } + + public void SendMessageToGroup(GridInstantMessage im, UUID groupID) + { + List groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID).ToString(), groupID); + int groupMembersCount = groupMembers.Count; + + if (m_messageOnlineAgentsOnly) + { + string[] t1 = groupMembers.ConvertAll(gmd => gmd.AgentID.ToString()).ToArray(); + + // We cache in order not to overwhlem the presence service on large grids with many groups. This does + // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed. + // (assuming this is the same across all grid simulators). + PresenceInfo[] onlineAgents; + if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents)) + { + onlineAgents = m_presenceService.GetAgents(t1); + m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds); + } + + HashSet onlineAgentsUuidSet = new HashSet(); + Array.ForEach(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID)); + + groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList(); + + // if (m_debugEnabled) +// m_log.DebugFormat( +// "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members, {2} online", +// groupID, groupMembersCount, groupMembers.Count()); + } + else + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members", + groupID, groupMembers.Count); + } + + int requestStartTick = Environment.TickCount; + + foreach (GroupMembersData member in groupMembers) + { + if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID.ToString(), groupID)) + { + // Don't deliver messages to people who have dropped this session + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} has dropped session, not delivering to them", member.AgentID); + continue; + } + + // Copy Message + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = groupID.Guid; + msg.fromAgentName = im.fromAgentName; + msg.message = im.message; + msg.dialog = im.dialog; + msg.offline = im.offline; + msg.ParentEstateID = im.ParentEstateID; + msg.Position = im.Position; + msg.RegionID = im.RegionID; + msg.binaryBucket = im.binaryBucket; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + + msg.fromAgentID = im.fromAgentID; + msg.fromGroup = true; + + msg.toAgentID = member.AgentID.Guid; + + IClientAPI client = GetActiveClient(member.AgentID); + if (client == null) + { + // If they're not local, forward across the grid + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} via Grid", member.AgentID); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + else + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); + ProcessMessageFromGroupSession(msg); + } + } + + // Temporary for assessing how long it still takes to send messages to large online groups. + if (m_messageOnlineAgentsOnly) + m_log.DebugFormat( + "[Groups.Messaging]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms", + groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick); + } + + #region SimGridEventHandlers + + void OnClientLogin(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name); + } + + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name); + + client.OnInstantMessage += OnInstantMessage; + } + + private void OnGridInstantMessage(GridInstantMessage msg) + { + // The instant message module will only deliver messages of dialog types: + // MessageFromAgent, StartTyping, StopTyping, MessageFromObject + // + // Any other message type will not be delivered to a client by the + // Instant Message Module + + + if (m_debugEnabled) + { + m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(msg); + } + + // Incoming message from a group + if ((msg.fromGroup == true) && + ((msg.dialog == (byte)InstantMessageDialog.SessionSend) + || (msg.dialog == (byte)InstantMessageDialog.SessionAdd) + || (msg.dialog == (byte)InstantMessageDialog.SessionDrop))) + { + ProcessMessageFromGroupSession(msg); + } + } + + private void ProcessMessageFromGroupSession(GridInstantMessage msg) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID); + + UUID AgentID = new UUID(msg.fromAgentID); + UUID GroupID = new UUID(msg.imSessionID); + + switch (msg.dialog) + { + case (byte)InstantMessageDialog.SessionAdd: + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + break; + + case (byte)InstantMessageDialog.SessionDrop: + m_groupData.AgentDroppedFromGroupChatSession(AgentID.ToString(), GroupID); + break; + + case (byte)InstantMessageDialog.SessionSend: + if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID) + && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID.ToString(), GroupID) + ) + { + // Agent not in session and hasn't dropped from session + // Add them to the session for now, and Invite them + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + + UUID toAgentID = new UUID(msg.toAgentID); + IClientAPI activeClient = GetActiveClient(toAgentID); + if (activeClient != null) + { + GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null); + if (groupInfo != null) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Sending chatterbox invite instant message"); + + // Force? open the group session dialog??? + // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg); + IEventQueue eq = activeClient.Scene.RequestModuleInterface(); + eq.ChatterboxInvitation( + GroupID + , groupInfo.GroupName + , new UUID(msg.fromAgentID) + , msg.message + , new UUID(msg.toAgentID) + , msg.fromAgentName + , msg.dialog + , msg.timestamp + , msg.offline == 1 + , (int)msg.ParentEstateID + , msg.Position + , 1 + , new UUID(msg.imSessionID) + , msg.fromGroup + , OpenMetaverse.Utils.StringToBytes(groupInfo.GroupName) + ); + + eq.ChatterBoxSessionAgentListUpdates( + new UUID(GroupID) + , new UUID(msg.fromAgentID) + , new UUID(msg.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + } + else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID)) + { + // User hasn't dropped, so they're in the session, + // maybe we should deliver it. + IClientAPI client = GetActiveClient(new UUID(msg.toAgentID)); + if (client != null) + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} locally", client.Name); + client.SendInstantMessage(msg); + } + else + { + m_log.WarnFormat("[Groups.Messaging]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID); + } + } + break; + + default: + m_log.WarnFormat("[Groups.Messaging]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString()); + break; + } + } + + #endregion + + + #region ClientEvents + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) + { + m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(im); + } + + // Start group IM session + if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups.Messaging]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID); + + UUID GroupID = new UUID(im.imSessionID); + UUID AgentID = new UUID(im.fromAgentID); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null); + + if (groupInfo != null) + { + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + + ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + queue.ChatterBoxSessionAgentListUpdates( + GroupID + , AgentID + , new UUID(im.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + + // Send a message from locally connected client to a group + if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) + { + UUID GroupID = new UUID(im.imSessionID); + UUID AgentID = new UUID(im.fromAgentID); + + if (m_debugEnabled) + m_log.DebugFormat("[Groups.Messaging]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString()); + + //If this agent is sending a message, then they want to be in the session + m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID); + + SendMessageToGroup(im, GroupID); + } + } + + #endregion + + void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + OSDMap moderatedMap = new OSDMap(4); + moderatedMap.Add("voice", OSD.FromBoolean(false)); + + OSDMap sessionMap = new OSDMap(4); + sessionMap.Add("moderated_mode", moderatedMap); + sessionMap.Add("session_name", OSD.FromString(groupName)); + sessionMap.Add("type", OSD.FromInteger(0)); + sessionMap.Add("voice_enabled", OSD.FromBoolean(false)); + + OSDMap bodyMap = new OSDMap(4); + bodyMap.Add("session_id", OSD.FromUUID(groupID)); + bodyMap.Add("temp_session_id", OSD.FromUUID(groupID)); + bodyMap.Add("success", OSD.FromBoolean(true)); + bodyMap.Add("session_info", sessionMap); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + + if (queue != null) + { + queue.Enqueue(queue.BuildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId); + } + } + + private void DebugGridInstantMessage(GridInstantMessage im) + { + // Don't log any normal IMs (privacy!) + if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent) + { + m_log.WarnFormat("[Groups.Messaging]: IM: fromGroup({0})", im.fromGroup ? "True" : "False"); + m_log.WarnFormat("[Groups.Messaging]: IM: Dialog({0})", ((InstantMessageDialog)im.dialog).ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentID({0})", im.fromAgentID.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentName({0})", im.fromAgentName.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: imSessionID({0})", im.imSessionID.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: message({0})", im.message.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: offline({0})", im.offline.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: toAgentID({0})", im.toAgentID.ToString()); + m_log.WarnFormat("[Groups.Messaging]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket")); + } + } + + #region Client Tools + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + private IClientAPI GetActiveClient(UUID agentID) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Looking for local client {0}", agentID); + + IClientAPI child = null; + + // Try root avatar first + foreach (Scene scene in m_sceneList) + { + ScenePresence sp = scene.GetScenePresence(agentID); + if (sp != null) + { + if (!sp.IsChildAgent) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Found root agent for client : {0}", sp.ControllingClient.Name); + return sp.ControllingClient; + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Found child agent for client : {0}", sp.ControllingClient.Name); + child = sp.ControllingClient; + } + } + } + + // If we didn't find a root, then just return whichever child we found, or null if none + if (child == null) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Could not find local client for agent : {0}", agentID); + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Returning child agent for client : {0}", child.Name); + } + return child; + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/GroupsModule.cs b/OpenSim/Addons/Groups/GroupsModule.cs new file mode 100644 index 0000000..10bfa8f --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsModule.cs @@ -0,0 +1,1467 @@ +/* + * 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.Reflection; +using System.Timers; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsModule")] + public class GroupsModule : ISharedRegionModule, IGroupsModule + { + /// + /// + + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_sceneList = new List(); + + private IMessageTransferModule m_msgTransferModule = null; + + private IGroupsServicesConnector m_groupData = null; + private IUserManagement m_UserManagement; + + // Configuration settings + private bool m_groupsEnabled = false; + private bool m_groupNoticesEnabled = true; + private bool m_debugEnabled = false; + private int m_levelGroupCreate = 0; + + #region Region Module interfaceBase Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + if (groupsConfig == null) + { + // Do not run this module by default. + return; + } + else + { + m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false); + if (!m_groupsEnabled) + { + return; + } + + if (groupsConfig.GetString("Module", "Default") != Name) + { + m_groupsEnabled = false; + + return; + } + + m_log.InfoFormat("[Groups]: Initializing {0}", this.Name); + + m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true); + m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false); + m_levelGroupCreate = groupsConfig.GetInt("LevelGroupCreate", 0); + } + } + + public void AddRegion(Scene scene) + { + if (m_groupsEnabled) + { + scene.RegisterModuleInterface(this); + scene.AddCommand( + "debug", + this, + "debug groups verbose", + "debug groups verbose ", + "This setting turns on very verbose groups debugging", + HandleDebugGroupsVerbose); + } + } + + private void HandleDebugGroupsVerbose(object modules, string[] args) + { + if (args.Length < 4) + { + MainConsole.Instance.Output("Usage: debug groups verbose "); + return; + } + + bool verbose = false; + if (!bool.TryParse(args[3], out verbose)) + { + MainConsole.Instance.Output("Usage: debug groups verbose "); + return; + } + + m_debugEnabled = verbose; + + MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled); + } + + public void RegionLoaded(Scene scene) + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + // The InstantMessageModule itself doesn't do this, + // so lets see if things explode if we don't do it + // scene.EventManager.OnClientClosed += OnClientClosed; + + if (m_groupData == null) + { + m_groupData = scene.RequestModuleInterface(); + + // No Groups Service Connector, then nothing works... + if (m_groupData == null) + { + m_groupsEnabled = false; + m_log.Error("[Groups]: Could not get IGroupsServicesConnector"); + RemoveRegion(scene); + return; + } + } + + if (m_msgTransferModule == null) + { + m_msgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no notices, group invites, rejects, ejects, etc + if (m_msgTransferModule == null) + { + m_log.Warn("[Groups]: Could not get MessageTransferModule"); + } + } + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + if (m_UserManagement == null) + m_log.Warn("[Groups]: Could not get UserManagementModule"); + } + + lock (m_sceneList) + { + m_sceneList.Add(scene); + } + + + } + + public void RemoveRegion(Scene scene) + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; + + lock (m_sceneList) + { + m_sceneList.Remove(scene); + } + } + + public void Close() + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.Debug("[Groups]: Shutting down Groups module."); + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "Groups Module V2"; } + } + + public void PostInitialise() + { + // NoOp + } + + #endregion + + #region EventHandlers + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; + client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; + client.OnDirFindQuery += OnDirFindQuery; + client.OnRequestAvatarProperties += OnRequestAvatarProperties; + + // Used for Notices and Group Invites/Accept/Reject + client.OnInstantMessage += OnInstantMessage; + + // Send client their groups information. + SendAgentGroupDataUpdate(client, client.AgentId); + } + + private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + //GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetRequestingAgentID(remoteClient), avatarID).ToArray(); + GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID); + remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups); + } + + /* + * This becomes very problematic in a shared module. In a shared module you may have more then one + * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections. + * The OnClientClosed event does not provide anything to indicate which one of those should be closed + * nor does it provide what scene it was from so that the specific reference can be looked up. + * The InstantMessageModule.cs does not currently worry about unregistering the handles, + * and it should be an issue, since it's the client that references us not the other way around + * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed + private void OnClientClosed(UUID AgentId) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + lock (m_ActiveClients) + { + if (m_ActiveClients.ContainsKey(AgentId)) + { + IClientAPI client = m_ActiveClients[AgentId]; + client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest; + client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest; + client.OnDirFindQuery -= OnDirFindQuery; + client.OnInstantMessage -= OnInstantMessage; + + m_ActiveClients.Remove(AgentId); + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[Groups]: Client closed that wasn't registered here."); + } + + + } + } + */ + + void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart) + { + if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})", + System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart); + + // TODO: This currently ignores pretty much all the query flags including Mature and sort order + remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentIDStr(remoteClient), queryText).ToArray()); + } + + } + + private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + UUID activeGroupID = UUID.Zero; + string activeGroupTitle = string.Empty; + string activeGroupName = string.Empty; + ulong activeGroupPowers = (ulong)GroupPowers.None; + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetRequestingAgentIDStr(remoteClient), dataForAgentID.ToString()); + if (membership != null) + { + activeGroupID = membership.GroupID; + activeGroupTitle = membership.GroupTitle; + activeGroupPowers = membership.GroupPowers; + } + + SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle); + + SendScenePresenceUpdate(dataForAgentID, activeGroupTitle); + } + + private void HandleUUIDGroupNameRequest(UUID GroupID, IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string GroupName; + + GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null); + if (group != null) + { + GroupName = group.GroupName; + } + else + { + GroupName = "Unknown"; + } + + remoteClient.SendGroupNameReply(GroupID, GroupName); + } + + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_log.DebugFormat("[Groups]: IM From {0} to {1} msg {2} type {3}", im.fromAgentID, im.toAgentID, im.message, (InstantMessageDialog)im.dialog); + // Group invitations + if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) + { + UUID inviteID = new UUID(im.imSessionID); + GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + if (inviteInfo == null) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups]: Received an Invite IM for an invite that does not exist {0}.", inviteID); + return; + } + + //m_log.DebugFormat("[XXX]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); + + UUID fromAgentID = new UUID(im.fromAgentID); + UUID invitee = UUID.Zero; + string tmp = string.Empty; + Util.ParseUniversalUserIdentifier(inviteInfo.AgentID, out invitee, out tmp, out tmp, out tmp, out tmp); + if ((inviteInfo != null) && (fromAgentID == invitee)) + { + // Accept + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) + { + //m_log.DebugFormat("[XXX]: Received an accept invite notice."); + + // and the sessionid is the role + string reason = string.Empty; + if (!m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), invitee.ToString(), inviteInfo.GroupID, inviteInfo.RoleID, string.Empty, out reason)) + remoteClient.SendAgentAlertMessage("Unable to add you to the group: " + reason, false); + else + { + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = UUID.Zero.Guid; + msg.toAgentID = invitee.Guid; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.fromAgentName = "Groups"; + msg.message = string.Format("You have been added to the group."); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox; + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + msg.binaryBucket = new byte[0]; + + OutgoingInstantMessage(msg, invitee); + + UpdateAllClientsWithGroupInfo(invitee); + } + + m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + } + + // Reject + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: Received a reject invite notice."); + m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID); + } + } + } + + // Group notices + if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) + { + if (!m_groupNoticesEnabled) + { + return; + } + + UUID GroupID = new UUID(im.toAgentID); + if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null) != null) + { + UUID NoticeID = UUID.Random(); + string Subject = im.message.Substring(0, im.message.IndexOf('|')); + string Message = im.message.Substring(Subject.Length + 1); + + InventoryItemBase item = null; + bool hasAttachment = false; + + if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) + { + hasAttachment = true; + string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); + binBucket = binBucket.Remove(0, 14).Trim(); + + OSD binBucketOSD = OSDParser.DeserializeLLSDXml(binBucket); + if (binBucketOSD is OSDMap) + { + OSDMap binBucketMap = (OSDMap)binBucketOSD; + + UUID itemID = binBucketMap["item_id"].AsUUID(); + UUID ownerID = binBucketMap["owner_id"].AsUUID(); + item = new InventoryItemBase(itemID, ownerID); + item = m_sceneList[0].InventoryService.GetItem(item); + } + else + m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType()); + } + + if (m_groupData.AddGroupNotice(GetRequestingAgentIDStr(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, + hasAttachment, + (byte)(item == null ? 0 : item.AssetType), + item == null ? null : item.Name, + item == null ? UUID.Zero : item.ID, + item == null ? UUID.Zero.ToString() : item.Owner.ToString())) + { + if (OnNewGroupNotice != null) + { + OnNewGroupNotice(GroupID, NoticeID); + } + + // Send notice out to everyone that wants notices + // Build notice IIM + GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); + foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), GroupID)) + { + if (member.AcceptNotices) + { + msg.toAgentID = member.AgentID.Guid; + OutgoingInstantMessage(msg, member.AgentID); + } + } + } + } + } + + if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) + { + if (im.binaryBucket.Length < 16) // Invalid + return; + + //// 16 bytes are the UUID. Maybe. + UUID folderID = new UUID(im.binaryBucket, 0); + UUID noticeID = new UUID(im.imSessionID); + + GroupNoticeInfo notice = m_groupData.GetGroupNotice(remoteClient.AgentId.ToString(), noticeID); + if (notice != null) + { + UUID giver = new UUID(im.toAgentID); + string tmp = string.Empty; + Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out giver, out tmp, out tmp, out tmp, out tmp); + + m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId); + InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId, + giver, notice.noticeData.AttachmentItemID); + + if (itemCopy == null) + { + remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false); + return; + } + + remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); + } + + } + + // Interop, received special 210 code for ejecting a group member + // this only works within the comms servers domain, and won't work hypergrid + // TODO:FIXME: Use a presense server of some kind to find out where the + // client actually is, and try contacting that region directly to notify them, + // or provide the notification via xmlrpc update queue + if ((im.dialog == 210)) + { + // This is sent from the region that the ejectee was ejected from + // if it's being delivered here, then the ejectee is here + // so we need to send local updates to the agent. + + UUID ejecteeID = new UUID(im.toAgentID); + + im.dialog = (byte)InstantMessageDialog.MessageFromAgent; + OutgoingInstantMessage(im, ejecteeID); + + IClientAPI ejectee = GetActiveClient(ejecteeID); + if (ejectee != null) + { + UUID groupID = new UUID(im.imSessionID); + ejectee.SendAgentDropGroup(groupID); + } + } + } + + private void OnGridInstantMessage(GridInstantMessage msg) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Trigger the above event handler + OnInstantMessage(null, msg); + + // If a message from a group arrives here, it may need to be forwarded to a local client + if (msg.fromGroup == true) + { + switch (msg.dialog) + { + case (byte)InstantMessageDialog.GroupInvitation: + case (byte)InstantMessageDialog.GroupNotice: + UUID toAgentID = new UUID(msg.toAgentID); + IClientAPI localClient = GetActiveClient(toAgentID); + if (localClient != null) + { + localClient.SendInstantMessage(msg); + } + break; + } + } + } + + #endregion + + #region IGroupsModule Members + + public event NewGroupNotice OnNewGroupNotice; + + public GroupRecord GetGroupRecord(UUID GroupID) + { + return m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null); + } + + public GroupRecord GetGroupRecord(string name) + { + return m_groupData.GetGroupRecord(UUID.Zero.ToString(), UUID.Zero, name); + } + + public void ActivateGroup(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + // Changing active group changes title, active powers, all kinds of things + // anyone who is in any region that can see this client, should probably be + // updated with new group info. At a minimum, they should get ScenePresence + // updated with new title. + UpdateAllClientsWithGroupInfo(remoteClient.AgentId); + } + + /// + /// Get the Role Titles for an Agent, for a specific group + /// + public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List agentRoles = m_groupData.GetAgentGroupRoles(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + List titles = new List(); + foreach (GroupRolesData role in agentRoles) + { + GroupTitlesData title = new GroupTitlesData(); + title.Name = role.Name; + if (agentMembership != null) + { + title.Selected = agentMembership.ActiveRole == role.RoleID; + } + title.UUID = role.RoleID; + + titles.Add(title); + } + + return titles; + } + + public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name); + + List data = m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupMembersData member in data) + { + m_log.DebugFormat("[Groups]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner); + } + } + + return data; + + } + + public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoles(GetRequestingAgentIDStr(remoteClient), groupID); + + return data; + } + + public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentIDStr(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupRoleMembersData member in data) + { + m_log.DebugFormat("[Groups]: Member({0}) - Role({1})", member.MemberID, member.RoleID); + } + } + return data; + } + + public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupProfileData profile = new GroupProfileData(); + + // just to get the OwnerRole... + ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), groupID, string.Empty); + GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + if (groupInfo != null) + { + profile.AllowPublish = groupInfo.AllowPublish; + profile.Charter = groupInfo.Charter; + profile.FounderID = groupInfo.FounderID; + profile.GroupID = groupID; + profile.GroupMembershipCount = groupInfo.MemberCount; + profile.GroupRolesCount = groupInfo.RoleCount; + profile.InsigniaID = groupInfo.GroupPicture; + profile.MaturePublish = groupInfo.MaturePublish; + profile.MembershipFee = groupInfo.MembershipFee; + profile.Money = 0; + profile.Name = groupInfo.GroupName; + profile.OpenEnrollment = groupInfo.OpenEnrollment; + profile.OwnerRole = groupInfo.OwnerRoleID; + profile.ShowInList = groupInfo.ShowInList; + } + if (memberInfo != null) + { + profile.MemberTitle = memberInfo.GroupTitle; + profile.PowersMask = memberInfo.GroupPowers; + } + + return profile; + } + + public GroupMembershipData[] GetMembershipData(UUID agentID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + return m_groupData.GetAgentGroupMemberships(UUID.Zero.ToString(), agentID.ToString()).ToArray(); + } + + public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: {0} called with groupID={1}, agentID={2}", + System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID); + + return m_groupData.GetAgentGroupMembership(UUID.Zero.ToString(), agentID.ToString(), groupID); + } + + public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Note: Permissions checking for modification rights is handled by the Groups Server/Service + string reason = string.Empty; + if (!m_groupData.UpdateGroup(GetRequestingAgentIDStr(remoteClient), groupID, charter, showInList, insigniaID, membershipFee, + openEnrollment, allowPublish, maturePublish, out reason)) + remoteClient.SendAgentAlertMessage(reason, false); + } + + public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile) + { + // Note: Permissions checking for modification rights is handled by the Groups Server/Service + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.UpdateMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, acceptNotices, listInProfile); + } + + public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called in {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Scene.RegionInfo.RegionName); + + if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), UUID.Zero, name) != null) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists."); + return UUID.Zero; + } + + // check user level + ScenePresence avatar = null; + Scene scene = (Scene)remoteClient.Scene; + scene.TryGetScenePresence(remoteClient.AgentId, out avatar); + + if (avatar != null) + { + if (avatar.UserLevel < m_levelGroupCreate) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, String.Format("Insufficient permissions to create a group. Requires level {0}", m_levelGroupCreate)); + return UUID.Zero; + } + } + + // check funds + // is there is a money module present ? + IMoneyModule money = scene.RequestModuleInterface(); + if (money != null) + { + // do the transaction, that is if the agent has got sufficient funds + if (!money.AmountCovered(remoteClient.AgentId, money.GroupCreationCharge)) { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "Insufficient funds to create a group."); + return UUID.Zero; + } + money.ApplyCharge(remoteClient.AgentId, money.GroupCreationCharge, "Group Creation"); + } + string reason = string.Empty; + UUID groupID = m_groupData.CreateGroup(remoteClient.AgentId, name, charter, showInList, insigniaID, membershipFee, openEnrollment, + allowPublish, maturePublish, remoteClient.AgentId, out reason); + + if (groupID != UUID.Zero) + { + remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly"); + + // Update the founder with new group information. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + else + remoteClient.SendCreateGroupReply(groupID, false, reason); + + return groupID; + } + + public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // ToDo: check if agent is a member of group and is allowed to see notices? + + List notices = m_groupData.GetGroupNotices(GetRequestingAgentIDStr(remoteClient), groupID); + List os_notices = new List(); + foreach (ExtendedGroupNoticeData n in notices) + { + GroupNoticeData osn = n.ToGroupNoticeData(); + os_notices.Add(osn); + } + + return os_notices.ToArray(); + } + + /// + /// Get the title of the agent's current role. + /// + public string GetGroupTitle(UUID avatarID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(UUID.Zero.ToString(), avatarID.ToString()); + if (membership != null) + { + return membership.GroupTitle; + } + return string.Empty; + } + + /// + /// Change the current Active Group Role for Agent + /// + public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroupRole(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, titleRoleID); + + // TODO: Not sure what all is needed here, but if the active group role change is for the group + // the client currently has set active, then we need to do a scene presence update too + // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID) + + UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient)); + } + + + public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Security Checks are handled in the Groups Service. + + switch ((OpenMetaverse.GroupRoleUpdate)updateType) + { + case OpenMetaverse.GroupRoleUpdate.Create: + string reason = string.Empty; + if (!m_groupData.AddGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, UUID.Random(), name, description, title, powers, out reason)) + remoteClient.SendAgentAlertMessage("Unable to create role: " + reason, false); + break; + + case OpenMetaverse.GroupRoleUpdate.Delete: + m_groupData.RemoveGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID); + break; + + case OpenMetaverse.GroupRoleUpdate.UpdateAll: + case OpenMetaverse.GroupRoleUpdate.UpdateData: + case OpenMetaverse.GroupRoleUpdate.UpdatePowers: + if (m_debugEnabled) + { + GroupPowers gp = (GroupPowers)powers; + m_log.DebugFormat("[Groups]: Role ({0}) updated with Powers ({1}) ({2})", name, powers.ToString(), gp.ToString()); + } + m_groupData.UpdateGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID, name, description, title, powers); + break; + + case OpenMetaverse.GroupRoleUpdate.NoUpdate: + default: + // No Op + break; + + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + // Todo: Security check + + switch (changes) + { + case 0: + // Add + m_groupData.AddAgentToGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID); + + break; + case 1: + // Remove + m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID); + + break; + default: + m_log.ErrorFormat("[Groups]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); + break; + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called for notice {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, groupNoticeID); + + //GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null); + + GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested); + //GridInstantMessage msg = new GridInstantMessage(); + //msg.imSessionID = UUID.Zero.Guid; + //msg.fromAgentID = data.GroupID.Guid; + //msg.toAgentID = GetRequestingAgentID(remoteClient).Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + //msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName; + //msg.message = data.noticeData.Subject + "|" + data.Message; + //msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested; + //msg.fromGroup = true; + //msg.offline = (byte)0; + //msg.ParentEstateID = 0; + //msg.Position = Vector3.Zero; + //msg.RegionID = UUID.Zero.Guid; + //msg.binaryBucket = data.BinaryBucket; + + OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); + } + + public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GridInstantMessage msg = new GridInstantMessage(); + byte[] bucket; + + msg.imSessionID = groupNoticeID.Guid; + msg.toAgentID = agentID.Guid; + msg.dialog = dialog; + // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + + GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID.ToString(), groupNoticeID); + if (info != null) + { + msg.fromAgentID = info.GroupID.Guid; + msg.timestamp = info.noticeData.Timestamp; + msg.fromAgentName = info.noticeData.FromName; + msg.message = info.noticeData.Subject + "|" + info.Message; + if (info.noticeData.HasAttachment) + { + byte[] name = System.Text.Encoding.UTF8.GetBytes(info.noticeData.AttachmentName); + bucket = new byte[19 + name.Length]; + bucket[0] = 1; // has attachment? + bucket[1] = info.noticeData.AttachmentType; // attachment type + name.CopyTo(bucket, 18); + } + else + { + bucket = new byte[19]; + bucket[0] = 0; // Has att? + bucket[1] = 0; // type + bucket[18] = 0; // null terminated + } + + info.GroupID.ToBytes(bucket, 2); + msg.binaryBucket = bucket; + } + else + { + m_log.DebugFormat("[Groups]: Group Notice {0} not found, composing empty message.", groupNoticeID); + msg.fromAgentID = UUID.Zero.Guid; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; + msg.fromAgentName = string.Empty; + msg.message = string.Empty; + msg.binaryBucket = new byte[0]; + } + + return msg; + } + + public void SendAgentGroupDataUpdate(IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Send agent information about his groups + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string reason = string.Empty; + // Should check to see if OpenEnrollment, or if there's an outstanding invitation + if (m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, UUID.Zero, string.Empty, out reason)) + { + + remoteClient.SendJoinGroupReply(groupID, true); + + // Should this send updates to everyone in the group? + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + else + remoteClient.SendJoinGroupReply(groupID, false); + } + + public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + remoteClient.SendLeaveGroupReply(groupID, true); + + remoteClient.SendAgentDropGroup(groupID); + + // SL sends out notifcations to the group messaging session that the person has left + // Should this also update everyone who is in the group? + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID) + { + EjectGroupMember(remoteClient, GetRequestingAgentID(remoteClient), groupID, ejecteeID); + } + + public void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Todo: Security check? + m_groupData.RemoveAgentFromGroup(agentID.ToString(), ejecteeID.ToString(), groupID); + + string agentName; + RegionInfo regionInfo; + + // remoteClient provided or just agentID? + if (remoteClient != null) + { + agentName = remoteClient.Name; + regionInfo = remoteClient.Scene.RegionInfo; + remoteClient.SendEjectGroupMemberReply(agentID, groupID, true); + } + else + { + IClientAPI client = GetActiveClient(agentID); + + if (client != null) + { + agentName = client.Name; + regionInfo = client.Scene.RegionInfo; + client.SendEjectGroupMemberReply(agentID, groupID, true); + } + else + { + regionInfo = m_sceneList[0].RegionInfo; + UserAccount acc = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID); + + if (acc != null) + { + agentName = acc.FirstName + " " + acc.LastName; + } + else + { + agentName = "Unknown member"; + } + } + } + + GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + + UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, ejecteeID); + if ((groupInfo == null) || (account == null)) + { + return; + } + + // Send Message to Ejectee + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = agentID.Guid; + // msg.fromAgentID = info.GroupID; + msg.toAgentID = ejecteeID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = agentName; + msg.message = string.Format("You have been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent; + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + OutgoingInstantMessage(msg, ejecteeID); + + // Message to ejector + // Interop, received special 210 code for ejecting a group member + // this only works within the comms servers domain, and won't work hypergrid + // TODO:FIXME: Use a presense server of some kind to find out where the + // client actually is, and try contacting that region directly to notify them, + // or provide the notification via xmlrpc update queue + + msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = agentID.Guid; + msg.toAgentID = agentID.Guid; + msg.timestamp = 0; + msg.fromAgentName = agentName; + if (account != null) + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, account.FirstName + " " + account.LastName); + } + else + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, "Unknown member"); + } + msg.dialog = (byte)210; //interop + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + OutgoingInstantMessage(msg, agentID); + + + // SL sends out messages to everyone in the group + // Who all should receive updates and what should they be updated with? + UpdateAllClientsWithGroupInfo(ejecteeID); + } + + public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID) + { + InviteGroup(remoteClient, GetRequestingAgentID(remoteClient), groupID, invitedAgentID, roleID); + } + + public void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string agentName = m_UserManagement.GetUserName(agentID); + RegionInfo regionInfo = m_sceneList[0].RegionInfo; + + GroupRecord group = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + if (group == null) + { + m_log.DebugFormat("[Groups]: No such group {0}", groupID); + return; + } + + // Todo: Security check, probably also want to send some kind of notification + UUID InviteID = UUID.Random(); + + if (m_groupData.AddAgentToGroupInvite(agentID.ToString(), InviteID, groupID, roleID, invitedAgentID.ToString())) + { + if (m_msgTransferModule != null) + { + Guid inviteUUID = InviteID.Guid; + + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = inviteUUID; + + // msg.fromAgentID = agentID.Guid; + msg.fromAgentID = groupID.Guid; + msg.toAgentID = invitedAgentID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = agentName; + msg.message = string.Format("{0} has invited you to join a group called {1}. There is no cost to join this group.", agentName, group.GroupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[20]; + + OutgoingInstantMessage(msg, invitedAgentID); + } + } + } + + #endregion + + #region Client/Update Tools + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + private IClientAPI GetActiveClient(UUID agentID) + { + IClientAPI child = null; + + // Try root avatar first + foreach (Scene scene in m_sceneList) + { + ScenePresence sp = scene.GetScenePresence(agentID); + if (sp != null) + { + if (!sp.IsChildAgent) + { + return sp.ControllingClient; + } + else + { + child = sp.ControllingClient; + } + } + } + + // If we didn't find a root, then just return whichever child we found, or null if none + return child; + } + + /// + /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'. + /// + private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + OSDArray AgentData = new OSDArray(1); + OSDMap AgentDataMap = new OSDMap(1); + AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID)); + AgentData.Add(AgentDataMap); + + + OSDArray GroupData = new OSDArray(data.Length); + OSDArray NewGroupData = new OSDArray(data.Length); + + foreach (GroupMembershipData membership in data) + { + if (GetRequestingAgentID(remoteClient) != dataForAgentID) + { + if (!membership.ListInProfile) + { + // If we're sending group info to remoteclient about another agent, + // filter out groups the other agent doesn't want to share. + continue; + } + } + + OSDMap GroupDataMap = new OSDMap(6); + OSDMap NewGroupDataMap = new OSDMap(1); + + GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID)); + GroupDataMap.Add("GroupPowers", OSD.FromULong(membership.GroupPowers)); + GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices)); + GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture)); + GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution)); + GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName)); + NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile)); + + GroupData.Add(GroupDataMap); + NewGroupData.Add(NewGroupDataMap); + } + + OSDMap llDataStruct = new OSDMap(3); + llDataStruct.Add("AgentData", AgentData); + llDataStruct.Add("GroupData", GroupData); + llDataStruct.Add("NewGroupData", NewGroupData); + + if (m_debugEnabled) + { + m_log.InfoFormat("[Groups]: {0}", OSDParser.SerializeJsonString(llDataStruct)); + } + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + + if (queue != null) + { + queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient)); + } + + } + + private void SendScenePresenceUpdate(UUID AgentID, string Title) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: Updating scene title for {0} with title: {1}", AgentID, Title); + + ScenePresence presence = null; + + foreach (Scene scene in m_sceneList) + { + presence = scene.GetScenePresence(AgentID); + if (presence != null) + { + if (presence.Grouptitle != Title) + { + presence.Grouptitle = Title; + + if (! presence.IsChildAgent) + presence.SendAvatarDataToAllAgents(); + } + } + } + } + + /// + /// Send updates to all clients who might be interested in groups data for dataForClientID + /// + private void UpdateAllClientsWithGroupInfo(UUID dataForClientID) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Probably isn't nessesary to update every client in every scene. + // Need to examine client updates and do only what's nessesary. + lock (m_sceneList) + { + foreach (Scene scene in m_sceneList) + { + scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); }); + } + } + } + + /// + /// Update remoteClient with group information about dataForAgentID + /// + private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + + OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero); + + // Need to send a group membership update to the client + // UDP version doesn't seem to behave nicely. But we're going to send it out here + // with an empty group membership to hopefully remove groups being displayed due + // to the core Groups Stub + //remoteClient.SendGroupMembership(new GroupMembershipData[0]); + + GroupMembershipData[] membershipArray = GetProfileListedGroupMemberships(remoteClient, dataForAgentID); + SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipArray); + //remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipArray); + if (remoteClient.AgentId == dataForAgentID) + remoteClient.RefreshGroupMembership(); + } + + /// + /// Get a list of groups memberships for the agent that are marked "ListInProfile" + /// (unless that agent has a godLike aspect, in which case get all groups) + /// + /// + /// + private GroupMembershipData[] GetProfileListedGroupMemberships(IClientAPI requestingClient, UUID dataForAgentID) + { + List membershipData = m_groupData.GetAgentGroupMemberships(requestingClient.AgentId.ToString(), dataForAgentID.ToString()); + GroupMembershipData[] membershipArray; + + // cScene and property accessor 'isGod' are in support of the opertions to bypass 'hidden' group attributes for + // those with a GodLike aspect. + Scene cScene = (Scene)requestingClient.Scene; + bool isGod = cScene.Permissions.IsGod(requestingClient.AgentId); + + if (isGod) + { + membershipArray = membershipData.ToArray(); + } + else + { + if (requestingClient.AgentId != dataForAgentID) + { + Predicate showInProfile = delegate(GroupMembershipData membership) + { + return membership.ListInProfile; + }; + + membershipArray = membershipData.FindAll(showInProfile).ToArray(); + } + else + { + membershipArray = membershipData.ToArray(); + } + } + + if (m_debugEnabled) + { + m_log.InfoFormat("[Groups]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId); + foreach (GroupMembershipData membership in membershipArray) + { + m_log.InfoFormat("[Groups]: {0} :: {1} - {2} - {3}", dataForAgentID, membership.GroupName, membership.GroupTitle, membership.GroupPowers); + } + } + + return membershipArray; + } + + + private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, dataForAgentID); + string firstname, lastname; + if (account != null) + { + firstname = account.FirstName; + lastname = account.LastName; + } + else + { + firstname = "Unknown"; + lastname = "Unknown"; + } + + remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname, + lastname, activeGroupPowers, activeGroupName, + activeGroupTitle); + } + + #endregion + + #region IM Backed Processes + + private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + IClientAPI localClient = GetActiveClient(msgTo); + if (localClient != null) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is local, delivering directly", localClient.Name); + localClient.SendInstantMessage(msg); + } + else if (m_msgTransferModule != null) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[Groups]: Message Sent: {0}", success?"Succeeded":"Failed"); }); + } + } + + public void NotifyChange(UUID groupID) + { + // Notify all group members of a chnge in group roles and/or + // permissions + // + } + + #endregion + + private string GetRequestingAgentIDStr(IClientAPI client) + { + return GetRequestingAgentID(client).ToString(); + } + + private UUID GetRequestingAgentID(IClientAPI client) + { + UUID requestingAgentID = UUID.Zero; + if (client != null) + { + requestingAgentID = client.AgentId; + } + return requestingAgentID; + } + + } + +} diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs new file mode 100644 index 0000000..59fec6f --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs @@ -0,0 +1,289 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Server.Base; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Groups +{ + public class GroupsServiceHGConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI; + private object m_Lock = new object(); + + public GroupsServiceHGConnector(string url) + { + m_ServerURI = url; + if (!m_ServerURI.EndsWith("/")) + m_ServerURI += "/"; + + m_log.DebugFormat("[Groups.HGConnector]: Groups server at {0}", m_ServerURI); + } + + public bool CreateProxy(string RequestingAgentID, string AgentID, string accessToken, UUID groupID, string url, string name, out string reason) + { + reason = string.Empty; + + Dictionary sendData = new Dictionary(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AgentID"] = AgentID.ToString(); + sendData["AccessToken"] = accessToken; + sendData["GroupID"] = groupID.ToString(); + sendData["Location"] = url; + sendData["Name"] = name; + Dictionary ret = MakeRequest("POSTGROUP", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + { + reason = ret["REASON"].ToString(); + return false; + } + + return true; + + } + + public void RemoveAgentFromGroup(string AgentID, UUID GroupID, string token) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + MakeRequest("REMOVEAGENTFROMGROUP", sendData); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName, string token) + { + if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty))) + return null; + + Dictionary sendData = new Dictionary(); + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + if (GroupName != null && GroupName != string.Empty) + sendData["Name"] = GroupsDataUtils.Sanitize(GroupName); + + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + + Dictionary ret = MakeRequest("GETGROUP", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID, string token) + { + List members = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary ret = MakeRequest("GETGROUPMEMBERS", sendData); + + if (ret == null) + return members; + + if (!ret.ContainsKey("RESULT")) + return members; + + if (ret["RESULT"].ToString() == "NULL") + return members; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)v); + members.Add(m); + } + + return members; + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID, string token) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary ret = MakeRequest("GETGROUPROLES", sendData); + + if (ret == null) + return roles; + + if (!ret.ContainsKey("RESULT")) + return roles; + + if (ret["RESULT"].ToString() == "NULL") + return roles; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token) + { + List rmembers = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary ret = MakeRequest("GETROLEMEMBERS", sendData); + + if (ret == null) + return rmembers; + + if (!ret.ContainsKey("RESULT")) + return rmembers; + + if (ret["RESULT"].ToString() == "NULL") + return rmembers; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary)v); + rmembers.Add(m); + } + + return rmembers; + } + + public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["FromName"] = GroupsDataUtils.Sanitize(fromName); + sendData["Subject"] = GroupsDataUtils.Sanitize(subject); + sendData["Message"] = GroupsDataUtils.Sanitize(message); + sendData["HasAttachment"] = hasAttachment.ToString(); + if (hasAttachment) + { + sendData["AttachmentType"] = attType.ToString(); + sendData["AttachmentName"] = attName.ToString(); + sendData["AttachmentItemID"] = attItemID.ToString(); + sendData["AttachmentOwnerID"] = attOwnerID; + } + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("ADDNOTICE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public bool VerifyNotice(UUID noticeID, UUID groupID) + { + Dictionary sendData = new Dictionary(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["GroupID"] = groupID.ToString(); + Dictionary ret = MakeRequest("VERIFYNOTICE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + // + // + // + // + // + + #region Make Request + + private Dictionary MakeRequest(string method, Dictionary sendData) + { + sendData["METHOD"] = method; + + string reply = string.Empty; + lock (m_Lock) + reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "hg-groups", + ServerUtils.BuildQueryString(sendData)); + + //m_log.DebugFormat("[XXX]: reply was {0}", reply); + + if (reply == string.Empty || reply == null) + return null; + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs new file mode 100644 index 0000000..f670272 --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs @@ -0,0 +1,717 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Services.Interfaces; + +using OpenMetaverse; +using Mono.Addins; +using log4net; +using Nini.Config; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceHGConnectorModule")] + public class GroupsServiceHGConnectorModule : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private IGroupsServicesConnector m_LocalGroupsConnector; + private string m_LocalGroupsServiceLocation; + private IUserManagement m_UserManagement; + private IOfflineIMService m_OfflineIM; + private IMessageTransferModule m_Messaging; + private List m_Scenes; + private ForeignImporter m_ForeignImporter; + private string m_ServiceLocation; + private IConfigSource m_Config; + + private Dictionary m_NetworkConnectors = new Dictionary(); + private RemoteConnectorCacheWrapper m_CacheWrapper; // for caching info of external group services + + #region ISharedRegionModule + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + if (groupsConfig == null) + return; + + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name)) + { + return; + } + + m_Config = config; + m_ServiceLocation = groupsConfig.GetString("LocalService", "local"); // local or remote + m_LocalGroupsServiceLocation = groupsConfig.GetString("GroupsExternalURI", "http://127.0.0.1"); + m_Scenes = new List(); + + m_Enabled = true; + + m_log.DebugFormat("[Groups]: Initializing {0} with LocalService {1}", this.Name, m_ServiceLocation); + } + + public string Name + { + get { return "Groups HG Service Connector"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName); + scene.RegisterModuleInterface(this); + m_Scenes.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + m_OfflineIM = scene.RequestModuleInterface(); + m_Messaging = scene.RequestModuleInterface(); + m_ForeignImporter = new ForeignImporter(m_UserManagement); + + if (m_ServiceLocation.Equals("local")) + { + m_LocalGroupsConnector = new GroupsServiceLocalConnectorModule(m_Config, m_UserManagement); + // Also, if local, create the endpoint for the HGGroupsService + new HGGroupsServiceRobustConnector(m_Config, MainServer.Instance, string.Empty, + scene.RequestModuleInterface(), scene.RequestModuleInterface()); + + } + else + m_LocalGroupsConnector = new GroupsServiceRemoteConnectorModule(m_Config, m_UserManagement); + + m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement); + } + + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + #endregion + + private void OnNewClient(IClientAPI client) + { + client.OnCompleteMovementToRegion += OnCompleteMovementToRegion; + } + + void OnCompleteMovementToRegion(IClientAPI client, bool arg2) + { + object sp = null; + if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) + { + if (sp is ScenePresence && ((ScenePresence)sp).PresenceType != PresenceType.Npc) + { + AgentCircuitData aCircuit = ((ScenePresence)sp).Scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId); + if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 && + m_OfflineIM != null && m_Messaging != null) + { + List ims = m_OfflineIM.GetMessages(aCircuit.AgentID); + if (ims != null && ims.Count > 0) + foreach (GridInstantMessage im in ims) + m_Messaging.SendInstantMessage(im, delegate(bool success) { }); + } + } + } + } + + #region IGroupsServicesConnector + + public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + m_log.DebugFormat("[Groups]: Creating group {0}", name); + reason = string.Empty; + if (m_UserManagement.IsLocalGridUser(RequestingAgentID)) + return m_LocalGroupsConnector.CreateGroup(RequestingAgentID, name, charter, showInList, insigniaID, + membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason); + else + { + reason = "Only local grid users are allowed to create a new group"; + return UUID.Zero; + } + } + + public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason) + { + reason = string.Empty; + string url = string.Empty; + string name = string.Empty; + if (IsLocal(groupID, out url, out name)) + return m_LocalGroupsConnector.UpdateGroup(AgentUUI(RequestingAgentID), groupID, charter, showInList, insigniaID, membershipFee, + openEnrollment, allowPublish, maturePublish, out reason); + else + { + reason = "Changes to remote group not allowed. Please go to the group's original world."; + return false; + } + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + string url = string.Empty; + string name = string.Empty; + if (IsLocal(GroupID, out url, out name)) + return m_LocalGroupsConnector.GetGroupRecord(AgentUUI(RequestingAgentID), GroupID, GroupName); + else if (url != string.Empty) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + ExtendedGroupRecord grec = m_CacheWrapper.GetGroupRecord(RequestingAgentID, GroupID, GroupName, delegate + { + return c.GetGroupRecord(AgentUUIForOutside(RequestingAgentID), GroupID, GroupName, accessToken); + }); + + if (grec != null) + ImportForeigner(grec.FounderUUI); + return grec; + } + } + + return null; + } + + public List FindGroups(string RequestingAgentID, string search) + { + return m_LocalGroupsConnector.FindGroups(AgentUUI(RequestingAgentID), search); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + if (IsLocal(GroupID, out url, out gname)) + return m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), GroupID); + else if (!string.IsNullOrEmpty(url)) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + return m_CacheWrapper.GetGroupMembers(RequestingAgentID, GroupID, delegate + { + return c.GetGroupMembers(AgentUUIForOutside(RequestingAgentID), GroupID, accessToken); + }); + + } + } + return new List(); + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + reason = string.Empty; + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.AddGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers, out reason); + else + { + reason = "Operation not allowed outside this group's origin world."; + return false; + } + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.UpdateGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers); + else + { + return false; + } + + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + m_LocalGroupsConnector.RemoveGroupRole(AgentUUI(RequestingAgentID), groupID, roleID); + else + { + return; + } + } + + public List GetGroupRoles(string RequestingAgentID, UUID groupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.GetGroupRoles(AgentUUI(RequestingAgentID), groupID); + else if (!string.IsNullOrEmpty(url)) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + return m_CacheWrapper.GetGroupRoles(RequestingAgentID, groupID, delegate + { + return c.GetGroupRoles(AgentUUIForOutside(RequestingAgentID), groupID, accessToken); + }); + + } + } + + return new List(); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID groupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.GetGroupRoleMembers(AgentUUI(RequestingAgentID), groupID); + else if (!string.IsNullOrEmpty(url)) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID); + string accessToken = string.Empty; + if (membership != null) + accessToken = membership.AccessToken; + else + return null; + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + { + return m_CacheWrapper.GetGroupRoleMembers(RequestingAgentID, groupID, delegate + { + return c.GetGroupRoleMembers(AgentUUIForOutside(RequestingAgentID), groupID, accessToken); + }); + + } + } + + return new List(); + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + string url = string.Empty; + string name = string.Empty; + reason = string.Empty; + + UUID uid = new UUID(AgentID); + if (IsLocal(GroupID, out url, out name)) + { + if (m_UserManagement.IsLocalGridUser(uid)) // local user + { + // normal case: local group, local user + return m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason); + } + else // local group, foreign user + { + // the user is accepting the invitation, or joining, where the group resides + token = UUID.Random().ToString(); + bool success = m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason); + + if (success) + { + url = m_UserManagement.GetUserServerURL(uid, "GroupsServerURI"); + if (url == string.Empty) + { + reason = "User doesn't have a groups server"; + return false; + } + + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + return c.CreateProxy(AgentUUI(RequestingAgentID), AgentID, token, GroupID, m_LocalGroupsServiceLocation, name, out reason); + } + } + } + else if (m_UserManagement.IsLocalGridUser(uid)) // local user + { + // foreign group, local user. She's been added already by the HG service. + // Let's just check + if (m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID) != null) + return true; + } + + reason = "Operation not allowed outside this group's origin world"; + return false; + } + + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, name = string.Empty; + if (!IsLocal(GroupID, out url, out name) && url != string.Empty) + { + ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + if (membership != null) + { + GroupsServiceHGConnector c = GetConnector(url); + if (c != null) + c.RemoveAgentFromGroup(AgentUUIForOutside(AgentID), GroupID, membership.AccessToken); + } + } + + // remove from local service + m_LocalGroupsConnector.RemoveAgentFromGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + return m_LocalGroupsConnector.AddAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID, groupID, roleID, AgentUUI(agentID)); + else + return false; + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + return m_LocalGroupsConnector.GetAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID); ; + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_LocalGroupsConnector.RemoveAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID); + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.AddAgentToGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID); + + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.RemoveAgentFromGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID); + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + return m_LocalGroupsConnector.GetAgentGroupRoles(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + else + return new List(); + } + + public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.SetAgentActiveGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + return m_LocalGroupsConnector.GetAgentActiveMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID)); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + m_LocalGroupsConnector.SetAgentActiveGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + m_LocalGroupsConnector.UpdateMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, AcceptNotices, ListInProfile); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(GroupID, out url, out gname)) + return m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID); + else + return null; + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + return m_LocalGroupsConnector.GetAgentGroupMemberships(AgentUUI(RequestingAgentID), AgentUUI(AgentID)); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + string url = string.Empty, gname = string.Empty; + + if (IsLocal(groupID, out url, out gname)) + { + if (m_LocalGroupsConnector.AddGroupNotice(AgentUUI(RequestingAgentID), groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, AgentUUI(attOwnerID))) + { + // then send the notice to every grid for which there are members in this group + List members = m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), groupID); + List urls = new List(); + foreach (GroupMembersData m in members) + { + UUID userID = UUID.Zero; + if (!m_UserManagement.IsLocalGridUser(m.AgentID)) + { + string gURL = m_UserManagement.GetUserServerURL(m.AgentID, "GroupsServerURI"); + if (!urls.Contains(gURL)) + urls.Add(gURL); + } + } + + // so we have the list of urls to send the notice to + // this may take a long time... + Util.FireAndForget(delegate + { + foreach (string u in urls) + { + GroupsServiceHGConnector c = GetConnector(u); + if (c != null) + { + c.AddNotice(AgentUUIForOutside(RequestingAgentID), groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, AgentUUIForOutside(attOwnerID)); + } + } + }); + + return true; + } + + return false; + } + else + return false; + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + GroupNoticeInfo notice = m_LocalGroupsConnector.GetGroupNotice(AgentUUI(RequestingAgentID), noticeID); + + if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null) + ImportForeigner(notice.noticeData.AttachmentOwnerID); + + return notice; + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_LocalGroupsConnector.GetGroupNotices(AgentUUI(RequestingAgentID), GroupID); + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #endregion + + #region hypergrid groups + + private string AgentUUI(string AgentIDStr) + { + UUID AgentID = UUID.Zero; + try + { + AgentID = new UUID(AgentIDStr); + } + catch (FormatException) + { + return AgentID.ToString(); + } + + if (m_UserManagement.IsLocalGridUser(AgentID)) + return AgentID.ToString(); + + AgentCircuitData agent = null; + foreach (Scene scene in m_Scenes) + { + agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID); + if (agent != null) + break; + } + if (agent == null) // oops + return AgentID.ToString(); + + return Util.ProduceUserUniversalIdentifier(agent); + } + + private string AgentUUIForOutside(string AgentIDStr) + { + UUID AgentID = UUID.Zero; + try + { + AgentID = new UUID(AgentIDStr); + } + catch (FormatException) + { + return AgentID.ToString(); + } + + AgentCircuitData agent = null; + foreach (Scene scene in m_Scenes) + { + agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID); + if (agent != null) + break; + } + if (agent == null) // oops + return AgentID.ToString(); + + return Util.ProduceUserUniversalIdentifier(agent); + } + + private UUID ImportForeigner(string uID) + { + UUID userID = UUID.Zero; + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + if (Util.ParseUniversalUserIdentifier(uID, out userID, out url, out first, out last, out tmp)) + m_UserManagement.AddUser(userID, first, last, url); + + return userID; + } + + private bool IsLocal(UUID groupID, out string serviceLocation, out string name) + { + serviceLocation = string.Empty; + name = string.Empty; + ExtendedGroupRecord group = m_LocalGroupsConnector.GetGroupRecord(UUID.Zero.ToString(), groupID, string.Empty); + if (group == null) + { + //m_log.DebugFormat("[XXX]: IsLocal? group {0} not found -- no.", groupID); + return false; + } + + serviceLocation = group.ServiceLocation; + name = group.GroupName; + bool isLocal = (group.ServiceLocation == string.Empty); + //m_log.DebugFormat("[XXX]: IsLocal? {0}", isLocal); + return isLocal; + } + + private GroupsServiceHGConnector GetConnector(string url) + { + lock (m_NetworkConnectors) + { + if (m_NetworkConnectors.ContainsKey(url)) + return m_NetworkConnectors[url]; + + GroupsServiceHGConnector c = new GroupsServiceHGConnector(url); + m_NetworkConnectors[url] = c; + } + + return m_NetworkConnectors[url]; + } + #endregion + } +} diff --git a/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs new file mode 100644 index 0000000..92dd85c --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs @@ -0,0 +1,443 @@ +/* + * 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.Reflection; +using System.Text; +using System.Xml; +using System.Collections.Generic; +using System.IO; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public class HGGroupsServiceRobustConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private HGGroupsService m_GroupsService; + private string m_HomeURI = string.Empty; + private string m_ConfigName = "Groups"; + + // Called by Robust shell + public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) : + this(config, server, configName, null, null) + { + } + + // Called by the sim-bound module + public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName, IOfflineIMService im, IUserAccountService users) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + m_log.DebugFormat("[Groups.RobustHGConnector]: Starting with config name {0}", m_ConfigName); + + IConfig cnf = config.Configs[m_ConfigName]; + if (cnf == null) + throw new Exception(String.Format("[Groups.RobustHGConnector]: {0} section does not exist", m_ConfigName)); + + string homeURI = cnf.GetString("HomeURI", string.Empty); + if (homeURI == string.Empty) + throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide the HomeURI in section {0}", m_ConfigName)); + + if (im == null) + { + string imDll = cnf.GetString("OfflineIMService", string.Empty); + if (imDll == string.Empty) + throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide OfflineIMService in section {0}", m_ConfigName)); + + Object[] args = new Object[] { config }; + im = ServerUtils.LoadPlugin(imDll, args); + } + + if (users == null) + { + string usersDll = cnf.GetString("UserAccountService", string.Empty); + if (usersDll == string.Empty) + throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide UserAccountService in section {0}", m_ConfigName)); + + Object[] args = new Object[] { config }; + users = ServerUtils.LoadPlugin(usersDll, args); + } + + m_GroupsService = new HGGroupsService(config, im, users, homeURI); + + server.AddStreamHandler(new HGGroupsServicePostHandler(m_GroupsService)); + } + + } + + public class HGGroupsServicePostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private HGGroupsService m_GroupsService; + + public HGGroupsServicePostHandler(HGGroupsService service) : + base("POST", "/hg-groups") + { + m_GroupsService = service; + } + + public override byte[] Handle(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse 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(); + request.Remove("METHOD"); + + m_log.DebugFormat("[Groups.RobustHGConnector]: {0}", method); + switch (method) + { + case "POSTGROUP": + return HandleAddGroupProxy(request); + case "REMOVEAGENTFROMGROUP": + return HandleRemoveAgentFromGroup(request); + case "GETGROUP": + return HandleGetGroup(request); + case "ADDNOTICE": + return HandleAddNotice(request); + case "VERIFYNOTICE": + return HandleVerifyNotice(request); + case "GETGROUPMEMBERS": + return HandleGetGroupMembers(request); + case "GETGROUPROLES": + return HandleGetGroupRoles(request); + case "GETROLEMEMBERS": + return HandleGetRoleMembers(request); + + } + m_log.DebugFormat("[Groups.RobustHGConnector]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[Groups.RobustHGConnector]: Exception {0}", e.StackTrace); + } + + return FailureResult(); + } + + byte[] HandleAddGroupProxy(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") + || !request.ContainsKey("AgentID") + || !request.ContainsKey("AccessToken") || !request.ContainsKey("Location")) + NullResult(result, "Bad network data"); + + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + string agentID = request["AgentID"].ToString(); + UUID groupID = new UUID(request["GroupID"].ToString()); + string accessToken = request["AccessToken"].ToString(); + string location = request["Location"].ToString(); + string name = string.Empty; + if (request.ContainsKey("Name")) + name = request["Name"].ToString(); + + string reason = string.Empty; + bool success = m_GroupsService.CreateGroupProxy(RequestingAgentID, agentID, accessToken, groupID, location, name, out reason); + result["REASON"] = reason; + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleRemoveAgentFromGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("AccessToken") || !request.ContainsKey("AgentID") || + !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string agentID = request["AgentID"].ToString(); + string token = request["AccessToken"].ToString(); + string reason = string.Empty; + + m_GroupsService.RemoveAgentFromGroup(agentID, agentID, groupID, token); + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + result["RESULT"] = "true"; + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleGetGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + UUID groupID = UUID.Zero; + string groupName = string.Empty; + + if (request.ContainsKey("GroupID")) + groupID = new UUID(request["GroupID"].ToString()); + if (request.ContainsKey("Name")) + groupName = request["Name"].ToString(); + + ExtendedGroupRecord grec = m_GroupsService.GetGroupRecord(RequestingAgentID, groupID, groupName, token); + if (grec == null) + NullResult(result, "Group not found"); + else + result["RESULT"] = GroupsDataUtils.GroupRecord(grec); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroupMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + List members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID, token); + if (members == null || (members != null && members.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupMembersData m in members) + { + dict["m-" + i++] = GroupsDataUtils.GroupMembersData(m); + } + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroupRoles(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + List roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID, token); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupRolesData r in roles) + dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetRoleMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = request["AccessToken"].ToString(); + + List rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID, token); + if (rmembers == null || (rmembers != null && rmembers.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupRoleMembersData rm in rmembers) + dict["rm-" + i++] = GroupsDataUtils.GroupRoleMembersData(rm); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleAddNotice(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("NoticeID") || + !request.ContainsKey("FromName") || !request.ContainsKey("Subject") || !request.ContainsKey("Message") || + !request.ContainsKey("HasAttachment")) + NullResult(result, "Bad network data"); + + else + { + + bool hasAtt = bool.Parse(request["HasAttachment"].ToString()); + byte attType = 0; + string attName = string.Empty; + string attOwner = string.Empty; + UUID attItem = UUID.Zero; + if (request.ContainsKey("AttachmentType")) + attType = byte.Parse(request["AttachmentType"].ToString()); + if (request.ContainsKey("AttachmentName")) + attName = request["AttachmentType"].ToString(); + if (request.ContainsKey("AttachmentItemID")) + attItem = new UUID(request["AttachmentItemID"].ToString()); + if (request.ContainsKey("AttachmentOwnerID")) + attOwner = request["AttachmentOwnerID"].ToString(); + + bool success = m_GroupsService.AddNotice(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["NoticeID"].ToString()), request["FromName"].ToString(), request["Subject"].ToString(), + request["Message"].ToString(), hasAtt, attType, attName, attItem, attOwner); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleVerifyNotice(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("NoticeID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + + else + { + UUID noticeID = new UUID(request["NoticeID"].ToString()); + UUID groupID = new UUID(request["GroupID"].ToString()); + + bool success = m_GroupsService.VerifyNotice(noticeID, groupID); + //m_log.DebugFormat("[XXX]: VerifyNotice returned {0}", success); + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + // + // + // + // + // + + #region Helpers + + private void NullResult(Dictionary result, string reason) + { + result["RESULT"] = "NULL"; + result["REASON"] = reason; + } + + private byte[] FailureResult() + { + Dictionary result = new Dictionary(); + NullResult(result, "Unknown method"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/IGroupsServicesConnector.cs b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs new file mode 100644 index 0000000..73deb7a --- /dev/null +++ b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs @@ -0,0 +1,118 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Groups +{ + public interface IGroupsServicesConnector + { + UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID, out string reason); + bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason); + ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName); + List FindGroups(string RequestingAgentID, string search); + List GetGroupMembers(string RequestingAgentID, UUID GroupID); + + bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason); + bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers); + void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID); + List GetGroupRoles(string RequestingAgentID, UUID GroupID); + List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID); + + bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason); + void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID); + + bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID); + GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID); + void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID); + + void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID); + void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID); + List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID); + + void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID); + ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID); + + void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID); + void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile); + + /// + /// Get information about a specific group to which the user belongs. + /// + /// The agent requesting the information. + /// The agent requested. + /// The group requested. + /// + /// If the user is a member of the group then the data structure is returned. If not, then null is returned. + /// + ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID); + + /// + /// Get information about the groups to which a user belongs. + /// + /// The agent requesting the information. + /// The agent requested. + /// + /// Information about the groups to which the user belongs. If the user belongs to no groups then an empty + /// list is returned. + /// + List GetAgentGroupMemberships(string RequestingAgentID, string AgentID); + + bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID); + GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID); + List GetGroupNotices(string RequestingAgentID, UUID GroupID); + + void ResetAgentGroupChatSessions(string agentID); + bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID); + bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID); + void AgentDroppedFromGroupChatSession(string agentID, UUID groupID); + void AgentInvitedToGroupChatSession(string agentID, UUID groupID); + + } + + public class GroupInviteInfo + { + public UUID GroupID = UUID.Zero; + public UUID RoleID = UUID.Zero; + public string AgentID = string.Empty; + public UUID InviteID = UUID.Zero; + } + + public class GroupNoticeInfo + { + public ExtendedGroupNoticeData noticeData = new ExtendedGroupNoticeData(); + public UUID GroupID = UUID.Zero; + public string Message = string.Empty; + } + +} diff --git a/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs new file mode 100644 index 0000000..905bc91 --- /dev/null +++ b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs @@ -0,0 +1,347 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; + +using OpenMetaverse; +using Mono.Addins; +using log4net; +using Nini.Config; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceLocalConnectorModule")] + public class GroupsServiceLocalConnectorModule : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private GroupsService m_GroupsService; + private IUserManagement m_UserManagement; + private List m_Scenes; + private ForeignImporter m_ForeignImporter; + + #region constructors + public GroupsServiceLocalConnectorModule() + { + } + + public GroupsServiceLocalConnectorModule(IConfigSource config, IUserManagement uman) + { + Init(config); + m_UserManagement = uman; + m_ForeignImporter = new ForeignImporter(uman); + } + #endregion + + private void Init(IConfigSource config) + { + m_GroupsService = new GroupsService(config); + m_Scenes = new List(); + } + + #region ISharedRegionModule + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + if (groupsConfig == null) + return; + + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name)) + { + return; + } + + Init(config); + m_Enabled = true; + + m_log.DebugFormat("[Groups]: Initializing {0}", this.Name); + } + + public string Name + { + get { return "Groups Local Service Connector"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName); + scene.RegisterModuleInterface(this); + m_Scenes.Add(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + m_ForeignImporter = new ForeignImporter(m_UserManagement); + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + #endregion + + #region IGroupsServicesConnector + + public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + m_log.DebugFormat("[Groups]: Creating group {0}", name); + reason = string.Empty; + return m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID, + membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason); + } + + public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason) + { + reason = string.Empty; + m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); + return true; + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + if (GroupID != UUID.Zero) + return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID); + else if (GroupName != null) + return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupName); + + return null; + } + + public List FindGroups(string RequestingAgentID, string search) + { + return m_GroupsService.FindGroups(RequestingAgentID, search); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List _members = m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID); + if (_members != null && _members.Count > 0) + { + List members = _members.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + return members; + } + + return new List(); + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + return m_GroupsService.AddGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, out reason); + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + return m_GroupsService.UpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers); + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + m_GroupsService.RemoveGroupRole(RequestingAgentID, groupID, roleID); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + List _rm = m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID); + if (_rm != null && _rm.Count > 0) + { + List rm = _rm.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupRoleMembersData)); + return rm; + } + + return new List(); + + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + return m_GroupsService.AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, token, out reason); + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_GroupsService.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + return m_GroupsService.AddAgentToGroupInvite(RequestingAgentID, inviteID, groupID, roleID, agentID); + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + return m_GroupsService.GetAgentToGroupInvite(RequestingAgentID, inviteID); ; + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_GroupsService.RemoveAgentToGroupInvite(RequestingAgentID, inviteID); + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_GroupsService.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_GroupsService.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_GroupsService.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID); + } + + public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_GroupsService.SetAgentActiveGroup(RequestingAgentID, AgentID, GroupID); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + return m_GroupsService.GetAgentActiveMembership(RequestingAgentID, AgentID); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_GroupsService.SetAgentActiveGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + m_GroupsService.UpdateMembership(RequestingAgentID, AgentID, GroupID, AcceptNotices, ListInProfile); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_GroupsService.GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID); ; + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + return m_GroupsService.GetAgentGroupMemberships(RequestingAgentID, AgentID); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + return m_GroupsService.AddGroupNotice(RequestingAgentID, groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, attOwnerID); + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + GroupNoticeInfo notice = m_GroupsService.GetGroupNotice(RequestingAgentID, noticeID); + + //if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null) + //{ + // UUID userID = UUID.Zero; + // string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + // Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out userID, out url, out first, out last, out tmp); + // if (url != string.Empty) + // m_UserManagement.AddUser(userID, first, last, url); + //} + + return notice; + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID); + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..166803f --- /dev/null +++ b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Mono.Addins; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenSim.Addons.Groups")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("OpenSim.Addons.Groups")] +[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("313d4865-d179-4735-9b5a-fe74885878b2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.7.5.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: Addin("OpenSim.Groups", "0.1")] +[assembly: AddinDependency("OpenSim", "0.5")] diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs new file mode 100644 index 0000000..04328c9 --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs @@ -0,0 +1,642 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Server.Base; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Groups +{ + public class GroupsServiceRemoteConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI; + private object m_Lock = new object(); + + public GroupsServiceRemoteConnector(string url) + { + m_ServerURI = url; + if (!m_ServerURI.EndsWith("/")) + m_ServerURI += "/"; + + m_log.DebugFormat("[Groups.RemoteConnector]: Groups server at {0}", m_ServerURI); + } + + public ExtendedGroupRecord CreateGroup(string RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + reason = string.Empty; + + ExtendedGroupRecord rec = new ExtendedGroupRecord(); + rec.AllowPublish = allowPublish; + rec.Charter = charter; + rec.FounderID = founderID; + rec.GroupName = name; + rec.GroupPicture = insigniaID; + rec.MaturePublish = maturePublish; + rec.MembershipFee = membershipFee; + rec.OpenEnrollment = openEnrollment; + rec.ShowInList = showInList; + + Dictionary sendData = GroupsDataUtils.GroupRecord(rec); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + Dictionary ret = MakeRequest("PUTGROUP", sendData); + + if (ret == null) + return null; + + if (ret["RESULT"].ToString() == "NULL") + { + reason = ret["REASON"].ToString(); + return null; + } + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + + } + + public ExtendedGroupRecord UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + ExtendedGroupRecord rec = new ExtendedGroupRecord(); + rec.AllowPublish = allowPublish; + rec.Charter = charter; + rec.GroupPicture = insigniaID; + rec.MaturePublish = maturePublish; + rec.GroupID = groupID; + rec.MembershipFee = membershipFee; + rec.OpenEnrollment = openEnrollment; + rec.ShowInList = showInList; + + Dictionary sendData = GroupsDataUtils.GroupRecord(rec); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "UPDATE"; + Dictionary ret = MakeRequest("PUTGROUP", sendData); + + if (ret == null || (ret != null && ret["RESULT"].ToString() == "NULL")) + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty))) + return null; + + Dictionary sendData = new Dictionary(); + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + if (GroupName != null && GroupName != string.Empty) + sendData["Name"] = GroupsDataUtils.Sanitize(GroupName); + + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("GETGROUP", sendData); + + if (ret == null || (ret != null && ret["RESULT"].ToString() == "NULL")) + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + } + + public GroupMembershipData AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + reason = string.Empty; + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = token; + Dictionary ret = MakeRequest("ADDAGENTTOGROUP", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + { + reason = ret["REASON"].ToString(); + return null; + } + + return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]); + + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("REMOVEAGENTFROMGROUP", sendData); + } + + public ExtendedGroupMembershipData GetMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETMEMBERSHIP", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]); + } + + public List GetMemberships(string RequestingAgentID, string AgentID) + { + List memberships = new List(); + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["ALL"] = "true"; + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETMEMBERSHIP", sendData); + + if (ret == null) + return memberships; + + if (!ret.ContainsKey("RESULT")) + return memberships; + + if (ret["RESULT"].ToString() == "NULL") + return memberships; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupMembershipData m = GroupsDataUtils.GroupMembershipData((Dictionary)v); + memberships.Add(m); + } + + return memberships; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List members = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETGROUPMEMBERS", sendData); + + if (ret == null) + return members; + + if (!ret.ContainsKey("RESULT")) + return members; + + if (ret["RESULT"].ToString() == "NULL") + return members; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)v); + members.Add(m); + } + + return members; + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + reason = string.Empty; + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["Name"] = GroupsDataUtils.Sanitize(name); + sendData["Description"] = GroupsDataUtils.Sanitize(description); + sendData["Title"] = GroupsDataUtils.Sanitize(title); + sendData["Powers"] = powers.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + Dictionary ret = MakeRequest("PUTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + { + reason = ret["REASON"].ToString(); + return false; + } + + return true; + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["Name"] = GroupsDataUtils.Sanitize(name); + sendData["Description"] = GroupsDataUtils.Sanitize(description); + sendData["Title"] = GroupsDataUtils.Sanitize(title); + sendData["Powers"] = powers.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "UPDATE"; + Dictionary ret = MakeRequest("PUTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("REMOVEROLE", sendData); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETGROUPROLES", sendData); + + if (ret == null) + return roles; + + if (!ret.ContainsKey("RESULT")) + return roles; + + if (ret["RESULT"].ToString() == "NULL") + return roles; + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + List rmembers = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETROLEMEMBERS", sendData); + + if (ret == null) + return rmembers; + + if (!ret.ContainsKey("RESULT")) + return rmembers; + + if (ret["RESULT"].ToString() == "NULL") + return rmembers; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary)v); + rmembers.Add(m); + } + + return rmembers; + } + + public bool AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + + Dictionary ret = MakeRequest("AGENTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public bool RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "DELETE"; + + Dictionary ret = MakeRequest("AGENTROLE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETAGENTROLES", sendData); + + if (ret == null) + return roles; + + if (!ret.ContainsKey("RESULT")) + return roles; + + if (ret["RESULT"].ToString() == "NULL") + return roles; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public GroupMembershipData SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "GROUP"; + + Dictionary ret = MakeRequest("SETACTIVE", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ROLE"; + + MakeRequest("SETACTIVE", sendData); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["AcceptNotices"] = AcceptNotices.ToString(); + sendData["ListInProfile"] = ListInProfile.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("UPDATEMEMBERSHIP", sendData); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + Dictionary sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["AgentID"] = agentID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + + Dictionary ret = MakeRequest("INVITE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") // it may return "NULL" + return false; + + return true; + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + Dictionary sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "GET"; + + Dictionary ret = MakeRequest("INVITE", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupInviteInfo((Dictionary)ret["RESULT"]); + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + Dictionary sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "DELETE"; + + MakeRequest("INVITE", sendData); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["FromName"] = GroupsDataUtils.Sanitize(fromName); + sendData["Subject"] = GroupsDataUtils.Sanitize(subject); + sendData["Message"] = GroupsDataUtils.Sanitize(message); + sendData["HasAttachment"] = hasAttachment.ToString(); + if (hasAttachment) + { + sendData["AttachmentType"] = attType.ToString(); + sendData["AttachmentName"] = attName.ToString(); + sendData["AttachmentItemID"] = attItemID.ToString(); + sendData["AttachmentOwnerID"] = attOwnerID; + } + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("ADDNOTICE", sendData); + + if (ret == null) + return false; + + if (!ret.ContainsKey("RESULT")) + return false; + + if (ret["RESULT"].ToString().ToLower() != "true") + return false; + + return true; + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + Dictionary sendData = new Dictionary(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("GETNOTICES", sendData); + + if (ret == null) + return null; + + if (!ret.ContainsKey("RESULT")) + return null; + + if (ret["RESULT"].ToString() == "NULL") + return null; + + return GroupsDataUtils.GroupNoticeInfo((Dictionary)ret["RESULT"]); + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + List notices = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary ret = MakeRequest("GETNOTICES", sendData); + + if (ret == null) + return notices; + + if (!ret.ContainsKey("RESULT")) + return notices; + + if (ret["RESULT"].ToString() == "NULL") + return notices; + + foreach (object v in ((Dictionary)ret["RESULT"]).Values) + { + ExtendedGroupNoticeData m = GroupsDataUtils.GroupNoticeData((Dictionary)v); + notices.Add(m); + } + + return notices; + } + + #region Make Request + + private Dictionary MakeRequest(string method, Dictionary sendData) + { + sendData["METHOD"] = method; + + string reply = string.Empty; + lock (m_Lock) + reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "groups", + ServerUtils.BuildQueryString(sendData)); + + if (reply == string.Empty) + return null; + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs new file mode 100644 index 0000000..d1c02db --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs @@ -0,0 +1,437 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Threading; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Server.Base; + +using OpenMetaverse; +using Mono.Addins; +using log4net; +using Nini.Config; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceRemoteConnectorModule")] + public class GroupsServiceRemoteConnectorModule : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private GroupsServiceRemoteConnector m_GroupsService; + private IUserManagement m_UserManagement; + private List m_Scenes; + + private RemoteConnectorCacheWrapper m_CacheWrapper; + + #region constructors + public GroupsServiceRemoteConnectorModule() + { + } + + public GroupsServiceRemoteConnectorModule(IConfigSource config, IUserManagement uman) + { + Init(config); + m_UserManagement = uman; + m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement); + + } + #endregion + + private void Init(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + string url = groupsConfig.GetString("GroupsServerURI", string.Empty); + if (url == string.Empty) + { + m_log.WarnFormat("[Groups.RemoteConnector]: Groups server URL not provided. Groups will not work."); + return; + } + + m_GroupsService = new GroupsServiceRemoteConnector(url); + m_Scenes = new List(); + + } + + #region ISharedRegionModule + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + if (groupsConfig == null) + return; + + if ((groupsConfig.GetBoolean("Enabled", false) == false) + || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name)) + { + return; + } + + Init(config); + + m_Enabled = true; + m_log.DebugFormat("[Groups.RemoteConnector]: Initializing {0}", this.Name); + } + + public string Name + { + get { return "Groups Remote Service Connector"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_log.DebugFormat("[Groups.RemoteConnector]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName); + scene.RegisterModuleInterface(this); + m_Scenes.Add(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement); + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + #endregion + + #region IGroupsServicesConnector + + public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + m_log.DebugFormat("[Groups.RemoteConnector]: Creating group {0}", name); + string r = string.Empty; + + UUID groupID = m_CacheWrapper.CreateGroup(RequestingAgentID, delegate + { + return m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID, + membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out r); + }); + + reason = r; + return groupID; + } + + public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, + bool openEnrollment, bool allowPublish, bool maturePublish, out string reason) + { + string r = string.Empty; + + bool success = m_CacheWrapper.UpdateGroup(groupID, delegate + { + return m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); + }); + + reason = r; + return success; + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName) + { + if (GroupID == UUID.Zero && (GroupName == null || GroupName != null && GroupName == string.Empty)) + return null; + + return m_CacheWrapper.GetGroupRecord(RequestingAgentID,GroupID,GroupName, delegate + { + return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID, GroupName); + }); + } + + public List FindGroups(string RequestingAgentID, string search) + { + // TODO! + return new List(); + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + string agentFullID = AgentID; + m_log.DebugFormat("[Groups.RemoteConnector]: Add agent {0} to group {1}", agentFullID, GroupID); + string r = string.Empty; + + bool success = m_CacheWrapper.AddAgentToGroup(RequestingAgentID, AgentID, GroupID, delegate + { + return m_GroupsService.AddAgentToGroup(RequestingAgentID, agentFullID, GroupID, RoleID, token, out r); + }); + + reason = r; + return success; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_CacheWrapper.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID, delegate + { + m_GroupsService.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + }); + + } + + public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + m_CacheWrapper.SetAgentActiveGroup(AgentID, delegate + { + return m_GroupsService.SetAgentActiveGroup(RequestingAgentID, AgentID, GroupID); + }); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + return m_CacheWrapper.GetAgentActiveMembership(AgentID, delegate + { + return m_GroupsService.GetMembership(RequestingAgentID, AgentID, UUID.Zero); + }); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_CacheWrapper.GetAgentGroupMembership(AgentID, GroupID, delegate + { + return m_GroupsService.GetMembership(RequestingAgentID, AgentID, GroupID); + }); + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + return m_CacheWrapper.GetAgentGroupMemberships(AgentID, delegate + { + return m_GroupsService.GetMemberships(RequestingAgentID, AgentID); + }); + } + + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupMembers(RequestingAgentID, GroupID, delegate + { + return m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID); + }); + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + string r = string.Empty; + bool success = m_CacheWrapper.AddGroupRole(roleID, description, name, powers, title, delegate + { + return m_GroupsService.AddGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, out r); + }); + + reason = r; + return success; + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + return m_CacheWrapper.UpdateGroupRole(groupID, roleID, name, description, title, powers, delegate + { + return m_GroupsService.UpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers); + }); + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + m_CacheWrapper.RemoveGroupRole(RequestingAgentID, groupID, roleID, delegate + { + m_GroupsService.RemoveGroupRole(RequestingAgentID, groupID, roleID); + }); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupRoles(RequestingAgentID, GroupID, delegate + { + return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID); + }); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupRoleMembers(RequestingAgentID, GroupID, delegate + { + return m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID); + }); + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_CacheWrapper.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID, delegate + { + return m_GroupsService.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + }); + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_CacheWrapper.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID, delegate + { + return m_GroupsService.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + }); + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + return m_CacheWrapper.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID, delegate + { + return m_GroupsService.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID); ; + }); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + m_CacheWrapper.SetAgentActiveGroupRole(AgentID, GroupID, delegate + { + m_GroupsService.SetAgentActiveGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + }); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + m_CacheWrapper.UpdateMembership(AgentID, GroupID, AcceptNotices, ListInProfile, delegate + { + m_GroupsService.UpdateMembership(RequestingAgentID, AgentID, GroupID, AcceptNotices, ListInProfile); + }); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + return m_GroupsService.AddAgentToGroupInvite(RequestingAgentID, inviteID, groupID, roleID, agentID); + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + return m_GroupsService.GetAgentToGroupInvite(RequestingAgentID, inviteID); + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_GroupsService.RemoveAgentToGroupInvite(RequestingAgentID, inviteID); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + GroupNoticeInfo notice = new GroupNoticeInfo(); + notice.GroupID = groupID; + notice.Message = message; + notice.noticeData = new ExtendedGroupNoticeData(); + notice.noticeData.AttachmentItemID = attItemID; + notice.noticeData.AttachmentName = attName; + notice.noticeData.AttachmentOwnerID = attOwnerID.ToString(); + notice.noticeData.AttachmentType = attType; + notice.noticeData.FromName = fromName; + notice.noticeData.HasAttachment = hasAttachment; + notice.noticeData.NoticeID = noticeID; + notice.noticeData.Subject = subject; + notice.noticeData.Timestamp = (uint)Util.UnixTimeSinceEpoch(); + + return m_CacheWrapper.AddGroupNotice(groupID, noticeID, notice, delegate + { + return m_GroupsService.AddGroupNotice(RequestingAgentID, groupID, noticeID, fromName, subject, message, + hasAttachment, attType, attName, attItemID, attOwnerID); + }); + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + return m_CacheWrapper.GetGroupNotice(noticeID, delegate + { + return m_GroupsService.GetGroupNotice(RequestingAgentID, noticeID); + }); + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupNotices(GroupID, delegate + { + return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID); + }); + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #endregion + } + +} diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs new file mode 100644 index 0000000..8c257ed --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs @@ -0,0 +1,760 @@ +/* + * 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.Reflection; +using System.Text; +using System.Xml; +using System.Collections.Generic; +using System.IO; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public class GroupsServiceRobustConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private GroupsService m_GroupsService; + private string m_ConfigName = "Groups"; + + public GroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + m_log.DebugFormat("[Groups.RobustConnector]: Starting with config name {0}", m_ConfigName); + + m_GroupsService = new GroupsService(config); + + server.AddStreamHandler(new GroupsServicePostHandler(m_GroupsService)); + } + } + + public class GroupsServicePostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private GroupsService m_GroupsService; + + public GroupsServicePostHandler(GroupsService service) : + base("POST", "/groups") + { + m_GroupsService = service; + } + + public override byte[] Handle(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse 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(); + request.Remove("METHOD"); + + m_log.DebugFormat("[Groups.Handler]: {0}", method); + switch (method) + { + case "PUTGROUP": + return HandleAddOrUpdateGroup(request); + case "GETGROUP": + return HandleGetGroup(request); + case "ADDAGENTTOGROUP": + return HandleAddAgentToGroup(request); + case "REMOVEAGENTFROMGROUP": + return HandleRemoveAgentFromGroup(request); + case "GETMEMBERSHIP": + return HandleGetMembership(request); + case "GETGROUPMEMBERS": + return HandleGetGroupMembers(request); + case "PUTROLE": + return HandlePutRole(request); + case "REMOVEROLE": + return HandleRemoveRole(request); + case "GETGROUPROLES": + return HandleGetGroupRoles(request); + case "GETROLEMEMBERS": + return HandleGetRoleMembers(request); + case "AGENTROLE": + return HandleAgentRole(request); + case "GETAGENTROLES": + return HandleGetAgentRoles(request); + case "SETACTIVE": + return HandleSetActive(request); + case "UPDATEMEMBERSHIP": + return HandleUpdateMembership(request); + case "INVITE": + return HandleInvite(request); + case "ADDNOTICE": + return HandleAddNotice(request); + case "GETNOTICES": + return HandleGetNotices(request); + } + m_log.DebugFormat("[GROUPS HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[GROUPS HANDLER]: Exception {0}", e.StackTrace); + } + + return FailureResult(); + } + + byte[] HandleAddOrUpdateGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + ExtendedGroupRecord grec = GroupsDataUtils.GroupRecord(request); + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("OP")) + NullResult(result, "Bad network data"); + + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + string reason = string.Empty; + string op = request["OP"].ToString(); + if (op == "ADD") + { + grec.GroupID = m_GroupsService.CreateGroup(RequestingAgentID, grec.GroupName, grec.Charter, grec.ShowInList, grec.GroupPicture, grec.MembershipFee, + grec.OpenEnrollment, grec.AllowPublish, grec.MaturePublish, grec.FounderID, out reason); + + } + else if (op == "UPDATE") + { + m_GroupsService.UpdateGroup(RequestingAgentID, grec.GroupID, grec.Charter, grec.ShowInList, grec.GroupPicture, grec.MembershipFee, + grec.OpenEnrollment, grec.AllowPublish, grec.MaturePublish); + + } + + grec = m_GroupsService.GetGroupRecord(RequestingAgentID, grec.GroupID); + if (grec == null) + NullResult(result, "Internal Error"); + else + result["RESULT"] = GroupsDataUtils.GroupRecord(grec); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID")) + NullResult(result, "Bad network data"); + else + { + string RequestingAgentID = request["RequestingAgentID"].ToString(); + ExtendedGroupRecord grec = null; + if (request.ContainsKey("GroupID")) + { + UUID groupID = new UUID(request["GroupID"].ToString()); + grec = m_GroupsService.GetGroupRecord(RequestingAgentID, groupID); + } + else if (request.ContainsKey("Name")) + { + string name = request["Name"].ToString(); + grec = m_GroupsService.GetGroupRecord(RequestingAgentID, name); + } + + if (grec == null) + NullResult(result, "Group not found"); + else + result["RESULT"] = GroupsDataUtils.GroupRecord(grec); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleAddAgentToGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || + !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + UUID roleID = new UUID(request["RoleID"].ToString()); + string agentID = request["AgentID"].ToString(); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string token = string.Empty; + string reason = string.Empty; + + if (request.ContainsKey("AccessToken")) + token = request["AccessToken"].ToString(); + + if (!m_GroupsService.AddAgentToGroup(requestingAgentID, agentID, groupID, roleID, token, out reason)) + NullResult(result, reason); + else + { + GroupMembershipData membership = m_GroupsService.GetAgentGroupMembership(requestingAgentID, agentID, groupID); + if (membership == null) + NullResult(result, "Internal error"); + else + result["RESULT"] = GroupsDataUtils.GroupMembershipData((ExtendedGroupMembershipData)membership); + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleRemoveAgentFromGroup(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string agentID = request["AgentID"].ToString(); + string requestingAgentID = request["RequestingAgentID"].ToString(); + string reason = string.Empty; + + m_GroupsService.RemoveAgentFromGroup(requestingAgentID, agentID, groupID); + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + result["RESULT"] = "true"; + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleGetMembership(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID")) + NullResult(result, "Bad network data"); + else + { + string agentID = request["AgentID"].ToString(); + UUID groupID = UUID.Zero; + if (request.ContainsKey("GroupID")) + groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + bool all = request.ContainsKey("ALL"); + + if (!all) + { + ExtendedGroupMembershipData membership = null; + if (groupID == UUID.Zero) + { + membership = m_GroupsService.GetAgentActiveMembership(requestingAgentID, agentID); + } + else + { + membership = m_GroupsService.GetAgentGroupMembership(requestingAgentID, agentID, groupID); + } + + if (membership == null) + NullResult(result, "No such membership"); + else + result["RESULT"] = GroupsDataUtils.GroupMembershipData(membership); + } + else + { + List memberships = m_GroupsService.GetAgentGroupMemberships(requestingAgentID, agentID); + if (memberships == null || (memberships != null && memberships.Count == 0)) + { + NullResult(result, "No memberships"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupMembershipData m in memberships) + dict["m-" + i++] = GroupsDataUtils.GroupMembershipData((ExtendedGroupMembershipData)m); + + result["RESULT"] = dict; + } + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetGroupMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID); + if (members == null || (members != null && members.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupMembersData m in members) + { + dict["m-" + i++] = GroupsDataUtils.GroupMembersData(m); + } + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandlePutRole(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID") || + !request.ContainsKey("Name") || !request.ContainsKey("Descrption") || !request.ContainsKey("Title") || + !request.ContainsKey("Powers") || !request.ContainsKey("OP")) + NullResult(result, "Bad network data"); + + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + bool success = false; + if (op == "ADD") + success = m_GroupsService.AddGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString()), request["Name"].ToString(), request["Description"].ToString(), + request["Title"].ToString(), UInt64.Parse(request["Powers"].ToString()), out reason); + + else if (op == "UPDATE") + success = m_GroupsService.UpdateGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString()), request["Name"].ToString(), request["Description"].ToString(), + request["Title"].ToString(), UInt64.Parse(request["Powers"].ToString())); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleRemoveRole(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID")) + NullResult(result, "Bad network data"); + + else + { + m_GroupsService.RemoveGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString())); + result["RESULT"] = "true"; + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleGetGroupRoles(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupRolesData r in roles) + dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetRoleMembers(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID); + if (rmembers == null || (rmembers != null && rmembers.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupRoleMembersData rm in rmembers) + dict["rm-" + i++] = GroupsDataUtils.GroupRoleMembersData(rm); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleAgentRole(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID") || + !request.ContainsKey("AgentID") || !request.ContainsKey("OP")) + NullResult(result, "Bad network data"); + + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + bool success = false; + if (op == "ADD") + success = m_GroupsService.AddAgentToGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), + new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString())); + + else if (op == "DELETE") + success = m_GroupsService.RemoveAgentFromGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), + new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString())); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetAgentRoles(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AgentID")) + NullResult(result, "Bad network data"); + else + { + UUID groupID = new UUID(request["GroupID"].ToString()); + string agentID = request["AgentID"].ToString(); + string requestingAgentID = request["RequestingAgentID"].ToString(); + + List roles = m_GroupsService.GetAgentGroupRoles(requestingAgentID, agentID, groupID); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (GroupRolesData r in roles) + dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r); + + result["RESULT"] = dict; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleSetActive(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || + !request.ContainsKey("AgentID") || !request.ContainsKey("OP")) + { + NullResult(result, "Bad network data"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + if (op == "GROUP") + { + ExtendedGroupMembershipData group = m_GroupsService.SetAgentActiveGroup(request["RequestingAgentID"].ToString(), + request["AgentID"].ToString(), new UUID(request["GroupID"].ToString())); + + if (group == null) + NullResult(result, "Internal error"); + else + result["RESULT"] = GroupsDataUtils.GroupMembershipData(group); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + + } + else if (op == "ROLE" && request.ContainsKey("RoleID")) + { + m_GroupsService.SetAgentActiveGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), + new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString())); + result["RESULT"] = "true"; + } + + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + } + + byte[] HandleUpdateMembership(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || !request.ContainsKey("GroupID") || + !request.ContainsKey("AcceptNotices") || !request.ContainsKey("ListInProfile")) + NullResult(result, "Bad network data"); + + else + { + m_GroupsService.UpdateMembership(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), new UUID(request["GroupID"].ToString()), + bool.Parse(request["AcceptNotices"].ToString()), bool.Parse(request["ListInProfile"].ToString())); + + result["RESULT"] = "true"; + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleInvite(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("InviteID")) + { + NullResult(result, "Bad network data"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + else + { + string op = request["OP"].ToString(); + string reason = string.Empty; + + if (op == "ADD" && request.ContainsKey("GroupID") && request.ContainsKey("RoleID") && request.ContainsKey("AgentID")) + { + bool success = m_GroupsService.AddAgentToGroupInvite(request["RequestingAgentID"].ToString(), + new UUID(request["InviteID"].ToString()), new UUID(request["GroupID"].ToString()), + new UUID(request["RoleID"].ToString()), request["AgentID"].ToString()); + + result["RESULT"] = success.ToString(); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + + } + else if (op == "DELETE") + { + m_GroupsService.RemoveAgentToGroupInvite(request["RequestingAgentID"].ToString(), new UUID(request["InviteID"].ToString())); + result["RESULT"] = "true"; + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + else if (op == "GET") + { + GroupInviteInfo invite = m_GroupsService.GetAgentToGroupInvite(request["RequestingAgentID"].ToString(), + new UUID(request["InviteID"].ToString())); + + result["RESULT"] = GroupsDataUtils.GroupInviteInfo(invite); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + NullResult(result, "Bad OP in request"); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + } + + byte[] HandleAddNotice(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("NoticeID") || + !request.ContainsKey("FromName") || !request.ContainsKey("Subject") || !request.ContainsKey("Message") || + !request.ContainsKey("HasAttachment")) + NullResult(result, "Bad network data"); + + else + { + + bool hasAtt = bool.Parse(request["HasAttachment"].ToString()); + byte attType = 0; + string attName = string.Empty; + string attOwner = string.Empty; + UUID attItem = UUID.Zero; + if (request.ContainsKey("AttachmentType")) + attType = byte.Parse(request["AttachmentType"].ToString()); + if (request.ContainsKey("AttachmentName")) + attName = request["AttachmentName"].ToString(); + if (request.ContainsKey("AttachmentItemID")) + attItem = new UUID(request["AttachmentItemID"].ToString()); + if (request.ContainsKey("AttachmentOwnerID")) + attOwner = request["AttachmentOwnerID"].ToString(); + + bool success = m_GroupsService.AddGroupNotice(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()), + new UUID(request["NoticeID"].ToString()), request["FromName"].ToString(), request["Subject"].ToString(), + request["Message"].ToString(), hasAtt, attType, attName, attItem, attOwner); + + result["RESULT"] = success.ToString(); + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetNotices(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID")) + NullResult(result, "Bad network data"); + + else if (request.ContainsKey("NoticeID")) // just one + { + GroupNoticeInfo notice = m_GroupsService.GetGroupNotice(request["RequestingAgentID"].ToString(), new UUID(request["NoticeID"].ToString())); + + if (notice == null) + NullResult(result, "NO such notice"); + else + result["RESULT"] = GroupsDataUtils.GroupNoticeInfo(notice); + + } + else if (request.ContainsKey("GroupID")) // all notices for group + { + List notices = m_GroupsService.GetGroupNotices(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString())); + + if (notices == null || (notices != null && notices.Count == 0)) + NullResult(result, "No notices"); + else + { + Dictionary dict = new Dictionary(); + int i = 0; + foreach (ExtendedGroupNoticeData n in notices) + dict["n-" + i++] = GroupsDataUtils.GroupNoticeData(n); + + result["RESULT"] = dict; + } + + } + else + NullResult(result, "Bad OP in request"); + + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + + #region Helpers + + private void NullResult(Dictionary result, string reason) + { + result["RESULT"] = "NULL"; + result["REASON"] = reason; + } + + private byte[] FailureResult() + { + Dictionary result = new Dictionary(); + NullResult(result, "Unknown method"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + #endregion + } +} diff --git a/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs b/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs new file mode 100644 index 0000000..f789626 --- /dev/null +++ b/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs @@ -0,0 +1,824 @@ +/* + * 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.Reflection; +using System.Threading; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +using OpenMetaverse; + +namespace OpenSim.Groups +{ + public delegate ExtendedGroupRecord GroupRecordDelegate(); + public delegate GroupMembershipData GroupMembershipDelegate(); + public delegate List GroupMembershipListDelegate(); + public delegate List GroupMembersListDelegate(); + public delegate List GroupRolesListDelegate(); + public delegate List RoleMembersListDelegate(); + public delegate GroupNoticeInfo NoticeDelegate(); + public delegate List NoticeListDelegate(); + public delegate void VoidDelegate(); + public delegate bool BooleanDelegate(); + + public class RemoteConnectorCacheWrapper + { + private ForeignImporter m_ForeignImporter; + + private Dictionary m_ActiveRequests = new Dictionary(); + private const int GROUPS_CACHE_TIMEOUT = 5 * 60; // 5 minutes + + // This all important cache cahces objects of different types: + // group- or group- => ExtendedGroupRecord + // active- => GroupMembershipData + // membership-- => GroupMembershipData + // memberships- => List + // members-- => List + // role- => GroupRolesData + // roles- => List ; all roles in the group + // roles-- => List ; roles that the agent has + // rolemembers-- => List + // notice- => GroupNoticeInfo + // notices- => List + private ExpiringCache m_Cache = new ExpiringCache(); + + public RemoteConnectorCacheWrapper(IUserManagement uman) + { + m_ForeignImporter = new ForeignImporter(uman); + } + + public UUID CreateGroup(UUID RequestingAgentID, GroupRecordDelegate d) + { + //m_log.DebugFormat("[Groups.RemoteConnector]: Creating group {0}", name); + //reason = string.Empty; + + //ExtendedGroupRecord group = m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID, + // membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason); + ExtendedGroupRecord group = d(); + + if (group == null) + return UUID.Zero; + + if (group.GroupID != UUID.Zero) + lock (m_Cache) + { + m_Cache.Add("group-" + group.GroupID.ToString(), group, GROUPS_CACHE_TIMEOUT); + if (m_Cache.Contains("memberships-" + RequestingAgentID.ToString())) + m_Cache.Remove("memberships-" + RequestingAgentID.ToString()); + } + + return group.GroupID; + } + + public bool UpdateGroup(UUID groupID, GroupRecordDelegate d) + { + //reason = string.Empty; + //ExtendedGroupRecord group = m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); + ExtendedGroupRecord group = d(); + + if (group != null && group.GroupID != UUID.Zero) + lock (m_Cache) + m_Cache.AddOrUpdate("group-" + group.GroupID.ToString(), group, GROUPS_CACHE_TIMEOUT); + return true; + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName, GroupRecordDelegate d) + { + //if (GroupID == UUID.Zero && (GroupName == null || GroupName != null && GroupName == string.Empty)) + // return null; + + object group = null; + bool firstCall = false; + string cacheKey = "group-"; + if (GroupID != UUID.Zero) + cacheKey += GroupID.ToString(); + else + cacheKey += GroupName; + + //m_log.DebugFormat("[XXX]: GetGroupRecord {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out group)) + { + //m_log.DebugFormat("[XXX]: GetGroupRecord {0} cached!", cacheKey); + return (ExtendedGroupRecord)group; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + //group = m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID, GroupName); + group = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, group, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (ExtendedGroupRecord)group; + } + } + else + Thread.Sleep(50); + } + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, GroupMembershipDelegate d) + { + GroupMembershipData membership = d(); + if (membership == null) + return false; + + lock (m_Cache) + { + // first, remove everything! add a user is a heavy-duty op + m_Cache.Clear(); + + m_Cache.AddOrUpdate("active-" + AgentID.ToString(), membership, GROUPS_CACHE_TIMEOUT); + m_Cache.AddOrUpdate("membership-" + AgentID.ToString() + "-" + GroupID.ToString(), membership, GROUPS_CACHE_TIMEOUT); + } + + + return true; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + string cacheKey = "active-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "memberships-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "members-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "roles-" + "-" + GroupID.ToString() + "-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + } + } + + public void SetAgentActiveGroup(string AgentID, GroupMembershipDelegate d) + { + GroupMembershipData activeGroup = d(); + if (activeGroup != null) + { + string cacheKey = "active-" + AgentID.ToString(); + lock (m_Cache) + if (m_Cache.Contains(cacheKey)) + m_Cache.AddOrUpdate(cacheKey, activeGroup, GROUPS_CACHE_TIMEOUT); + } + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string AgentID, GroupMembershipDelegate d) + { + object membership = null; + bool firstCall = false; + string cacheKey = "active-" + AgentID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentActiveMembership {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out membership)) + { + //m_log.DebugFormat("[XXX]: GetAgentActiveMembership {0} cached!", cacheKey); + return (ExtendedGroupMembershipData)membership; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + membership = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, membership, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (ExtendedGroupMembershipData)membership; + } + } + else + Thread.Sleep(50); + } + + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string AgentID, UUID GroupID, GroupMembershipDelegate d) + { + object membership = null; + bool firstCall = false; + string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupMembership {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out membership)) + { + //m_log.DebugFormat("[XXX]: GetAgentGroupMembership {0}", cacheKey); + return (ExtendedGroupMembershipData)membership; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + membership = d(); + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, membership, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (ExtendedGroupMembershipData)membership; + } + } + else + Thread.Sleep(50); + } + } + + public List GetAgentGroupMemberships(string AgentID, GroupMembershipListDelegate d) + { + object memberships = null; + bool firstCall = false; + string cacheKey = "memberships-" + AgentID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupMemberships {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out memberships)) + { + //m_log.DebugFormat("[XXX]: GetAgentGroupMemberships {0} cached!", cacheKey); + return (List)memberships; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + memberships = d(); + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, memberships, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)memberships; + } + } + else + Thread.Sleep(50); + } + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID, GroupMembersListDelegate d) + { + object members = null; + bool firstCall = false; + // we need to key in also on the requester, because different ppl have different view privileges + string cacheKey = "members-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetGroupMembers {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out members)) + { + List xx = (List)members; + return xx.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + List _members = d(); + + if (_members != null && _members.Count > 0) + members = _members.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + else + members = new List(); + + lock (m_Cache) + { + //m_Cache.AddOrUpdate(cacheKey, members, GROUPS_CACHE_TIMEOUT); + m_Cache.AddOrUpdate(cacheKey, _members, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + + return (List)members; + } + } + else + Thread.Sleep(50); + } + } + + public bool AddGroupRole(UUID roleID, string description, string name, ulong powers, string title, BooleanDelegate d) + { + if (d()) + { + GroupRolesData role = new GroupRolesData(); + role.Description = description; + role.Members = 0; + role.Name = name; + role.Powers = powers; + role.RoleID = roleID; + role.Title = title; + + lock (m_Cache) + m_Cache.AddOrUpdate("role-" + roleID.ToString(), role, GROUPS_CACHE_TIMEOUT); + + return true; + } + + return false; + } + + public bool UpdateGroupRole(UUID groupID, UUID roleID, string name, string description, string title, ulong powers, BooleanDelegate d) + { + if (d()) + { + object role; + lock (m_Cache) + if (m_Cache.TryGetValue("role-" + roleID.ToString(), out role)) + { + GroupRolesData r = (GroupRolesData)role; + r.Description = description; + r.Name = name; + r.Powers = powers; + r.Title = title; + + m_Cache.Update("role-" + roleID.ToString(), r, GROUPS_CACHE_TIMEOUT); + } + return true; + } + else + { + lock (m_Cache) + { + if (m_Cache.Contains("role-" + roleID.ToString())) + m_Cache.Remove("role-" + roleID.ToString()); + + // also remove these lists, because they will have an outdated role + if (m_Cache.Contains("roles-" + groupID.ToString())) + m_Cache.Remove("roles-" + groupID.ToString()); + + } + + return false; + } + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + if (m_Cache.Contains("role-" + roleID.ToString())) + m_Cache.Remove("role-" + roleID.ToString()); + + // also remove the list, because it will have an removed role + if (m_Cache.Contains("roles-" + groupID.ToString())) + m_Cache.Remove("roles-" + groupID.ToString()); + + if (m_Cache.Contains("roles-" + groupID.ToString() + "-" + RequestingAgentID.ToString())) + m_Cache.Remove("roles-" + groupID.ToString() + "-" + RequestingAgentID.ToString()); + + if (m_Cache.Contains("rolemembers-" + RequestingAgentID.ToString() + "-" + groupID.ToString())) + m_Cache.Remove("rolemembers-" + RequestingAgentID.ToString() + "-" + groupID.ToString()); + } + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID, GroupRolesListDelegate d) + { + object roles = null; + bool firstCall = false; + string cacheKey = "roles-" + GroupID.ToString(); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out roles)) + return (List)roles; + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + roles = d(); + if (roles != null) + { + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, roles, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)roles; + } + } + } + else + Thread.Sleep(50); + } + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, RoleMembersListDelegate d) + { + object rmembers = null; + bool firstCall = false; + // we need to key in also on the requester, because different ppl have different view privileges + string cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetGroupRoleMembers {0}", cacheKey); + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out rmembers)) + { + List xx = (List)rmembers; + return xx.ConvertAll(m_ForeignImporter.ConvertGroupRoleMembersData); + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + List _rmembers = d(); + + if (_rmembers != null && _rmembers.Count > 0) + rmembers = _rmembers.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupRoleMembersData)); + else + rmembers = new List(); + + lock (m_Cache) + { + // For some strange reason, when I cache the list of GroupRoleMembersData, + // it gets emptied out. The TryGet gets an empty list... + //m_Cache.AddOrUpdate(cacheKey, rmembers, GROUPS_CACHE_TIMEOUT); + // Caching the list of ExtendedGroupRoleMembersData doesn't show that issue + // I don't get it. + m_Cache.AddOrUpdate(cacheKey, _rmembers, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)rmembers; + } + } + else + Thread.Sleep(50); + } + } + + public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, BooleanDelegate d) + { + if (d()) + { + lock (m_Cache) + { + // update the cached role + string cacheKey = "role-" + RoleID.ToString(); + object obj; + if (m_Cache.TryGetValue(cacheKey, out obj)) + { + GroupRolesData r = (GroupRolesData)obj; + r.Members++; + } + + // add this agent to the list of role members + cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.TryGetValue(cacheKey, out obj)) + { + try + { + // This may throw an exception, in which case the agentID is not a UUID but a full ID + // In that case, let's just remove the whoe things from the cache + UUID id = new UUID(AgentID); + List xx = (List)obj; + List rmlist = xx.ConvertAll(m_ForeignImporter.ConvertGroupRoleMembersData); + GroupRoleMembersData rm = new GroupRoleMembersData(); + rm.MemberID = id; + rm.RoleID = RoleID; + rmlist.Add(rm); + } + catch + { + m_Cache.Remove(cacheKey); + } + } + + // Remove the cached info about this agent's roles + // because we don't have enough local info about the new role + cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + } + } + } + + public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, BooleanDelegate d) + { + if (d()) + { + lock (m_Cache) + { + // update the cached role + string cacheKey = "role-" + RoleID.ToString(); + object obj; + if (m_Cache.TryGetValue(cacheKey, out obj)) + { + GroupRolesData r = (GroupRolesData)obj; + r.Members--; + } + + cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + } + } + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID, GroupRolesListDelegate d) + { + object roles = null; + bool firstCall = false; + string cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out roles)) + { + //m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0} cached!", cacheKey); + return (List)roles; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + roles = d(); + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, roles, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)roles; + } + } + else + Thread.Sleep(50); + } + } + + public void SetAgentActiveGroupRole(string AgentID, UUID GroupID, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + // Invalidate cached info, because it has ActiveRoleID and Powers + string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "memberships-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + } + } + + public void UpdateMembership(string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile, VoidDelegate d) + { + d(); + + lock (m_Cache) + { + string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "memberships-" + AgentID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + cacheKey = "active-" + AgentID.ToString(); + object m = null; + if (m_Cache.TryGetValue(cacheKey, out m)) + { + GroupMembershipData membership = (GroupMembershipData)m; + membership.ListInProfile = ListInProfile; + membership.AcceptNotices = AcceptNotices; + } + } + } + + public bool AddGroupNotice(UUID groupID, UUID noticeID, GroupNoticeInfo notice, BooleanDelegate d) + { + if (d()) + { + lock (m_Cache) + { + m_Cache.AddOrUpdate("notice-" + noticeID.ToString(), notice, GROUPS_CACHE_TIMEOUT); + string cacheKey = "notices-" + groupID.ToString(); + if (m_Cache.Contains(cacheKey)) + m_Cache.Remove(cacheKey); + + } + + return true; + } + + return false; + } + + public GroupNoticeInfo GetGroupNotice(UUID noticeID, NoticeDelegate d) + { + object notice = null; + bool firstCall = false; + string cacheKey = "notice-" + noticeID.ToString(); + + //m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out notice)) + { + return (GroupNoticeInfo)notice; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + GroupNoticeInfo _notice = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, _notice, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return _notice; + } + } + else + Thread.Sleep(50); + } + } + + public List GetGroupNotices(UUID GroupID, NoticeListDelegate d) + { + object notices = null; + bool firstCall = false; + string cacheKey = "notices-" + GroupID.ToString(); + + //m_log.DebugFormat("[XXX]: GetGroupNotices {0}", cacheKey); + + while (true) + { + lock (m_Cache) + { + if (m_Cache.TryGetValue(cacheKey, out notices)) + { + //m_log.DebugFormat("[XXX]: GetGroupNotices {0} cached!", cacheKey); + return (List)notices; + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + notices = d(); + + lock (m_Cache) + { + m_Cache.AddOrUpdate(cacheKey, notices, GROUPS_CACHE_TIMEOUT); + m_ActiveRequests.Remove(cacheKey); + return (List)notices; + } + } + else + Thread.Sleep(50); + } + } + + + } +} diff --git a/OpenSim/Addons/Groups/Service/GroupsService.cs b/OpenSim/Addons/Groups/Service/GroupsService.cs new file mode 100644 index 0000000..fc567dd --- /dev/null +++ b/OpenSim/Addons/Groups/Service/GroupsService.cs @@ -0,0 +1,1014 @@ +/* + * 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.Reflection; +using System.Timers; +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Groups +{ + public class GroupsService : GroupsServiceBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const GroupPowers DefaultEveryonePowers = GroupPowers.AllowSetHome | + GroupPowers.Accountable | + GroupPowers.JoinChat | + GroupPowers.AllowVoiceChat | + GroupPowers.ReceiveNotices | + GroupPowers.StartProposal | + GroupPowers.VoteOnProposal; + + public const GroupPowers OwnerPowers = GroupPowers.Accountable | + GroupPowers.AllowEditLand | + GroupPowers.AllowFly | + GroupPowers.AllowLandmark | + GroupPowers.AllowRez | + GroupPowers.AllowSetHome | + GroupPowers.AllowVoiceChat | + GroupPowers.AssignMember | + GroupPowers.AssignMemberLimited | + GroupPowers.ChangeActions | + GroupPowers.ChangeIdentity | + GroupPowers.ChangeMedia | + GroupPowers.ChangeOptions | + GroupPowers.CreateRole | + GroupPowers.DeedObject | + GroupPowers.DeleteRole | + GroupPowers.Eject | + GroupPowers.FindPlaces | + GroupPowers.Invite | + GroupPowers.JoinChat | + GroupPowers.LandChangeIdentity | + GroupPowers.LandDeed | + GroupPowers.LandDivideJoin | + GroupPowers.LandEdit | + GroupPowers.LandEjectAndFreeze | + GroupPowers.LandGardening | + GroupPowers.LandManageAllowed | + GroupPowers.LandManageBanned | + GroupPowers.LandManagePasses | + GroupPowers.LandOptions | + GroupPowers.LandRelease | + GroupPowers.LandSetSale | + GroupPowers.ModerateChat | + GroupPowers.ObjectManipulate | + GroupPowers.ObjectSetForSale | + GroupPowers.ReceiveNotices | + GroupPowers.RemoveMember | + GroupPowers.ReturnGroupOwned | + GroupPowers.ReturnGroupSet | + GroupPowers.ReturnNonGroup | + GroupPowers.RoleProperties | + GroupPowers.SendNotices | + GroupPowers.SetLandingPoint | + GroupPowers.StartProposal | + GroupPowers.VoteOnProposal; + + #region Daily Cleanup + + private Timer m_CleanupTimer; + + public GroupsService(IConfigSource config, string configName) + : base(config, configName) + { + } + + public GroupsService(IConfigSource config) + : this(config, string.Empty) + { + // Once a day + m_CleanupTimer = new Timer(24 * 60 * 60 * 1000); + m_CleanupTimer.AutoReset = true; + m_CleanupTimer.Elapsed += new ElapsedEventHandler(m_CleanupTimer_Elapsed); + m_CleanupTimer.Enabled = true; + m_CleanupTimer.Start(); + } + + private void m_CleanupTimer_Elapsed(object sender, ElapsedEventArgs e) + { + m_Database.DeleteOldNotices(); + m_Database.DeleteOldInvites(); + } + + #endregion + + public UUID CreateGroup(string RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish, UUID founderID, out string reason) + { + reason = string.Empty; + + // Create the group + GroupData data = new GroupData(); + data.GroupID = UUID.Random(); + data.Data = new Dictionary(); + data.Data["Name"] = name; + data.Data["Charter"] = charter; + data.Data["InsigniaID"] = insigniaID.ToString(); + data.Data["FounderID"] = founderID.ToString(); + data.Data["MembershipFee"] = membershipFee.ToString(); + data.Data["OpenEnrollment"] = openEnrollment ? "1" : "0"; + data.Data["ShowInList"] = showInList ? "1" : "0"; + data.Data["AllowPublish"] = allowPublish ? "1" : "0"; + data.Data["MaturePublish"] = maturePublish ? "1" : "0"; + data.Data["OwnerRoleID"] = UUID.Random().ToString(); + + if (!m_Database.StoreGroup(data)) + return UUID.Zero; + + // Create Everyone role + _AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, UUID.Zero, "Everyone", "Everyone in the group", "Member of " + name, (ulong)DefaultEveryonePowers, true); + + // Create Owner role + UUID roleID = UUID.Random(); + _AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, roleID, "Owners", "Owners of the group", "Owner of " + name, (ulong)OwnerPowers, true); + + // Add founder to group + _AddAgentToGroup(RequestingAgentID, founderID.ToString(), data.GroupID, roleID); + + return data.GroupID; + } + + public void UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + GroupData data = m_Database.RetrieveGroup(groupID); + if (data == null) + return; + + // Check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.ChangeActions)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at updating group {1} denied because of lack of permission", RequestingAgentID, groupID); + return; + } + + data.GroupID = groupID; + data.Data["Charter"] = charter; + data.Data["ShowInList"] = showInList ? "1" : "0"; + data.Data["InsigniaID"] = insigniaID.ToString(); + data.Data["MembershipFee"] = membershipFee.ToString(); + data.Data["OpenEnrollment"] = openEnrollment ? "1" : "0"; + data.Data["AllowPublish"] = allowPublish ? "1" : "0"; + data.Data["MaturePublish"] = maturePublish ? "1" : "0"; + + m_Database.StoreGroup(data); + + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID) + { + GroupData data = m_Database.RetrieveGroup(GroupID); + + return _GroupDataToRecord(data); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, string GroupName) + { + GroupData data = m_Database.RetrieveGroup(GroupName); + + return _GroupDataToRecord(data); + } + + public List FindGroups(string RequestingAgentID, string search) + { + List groups = new List(); + + GroupData[] data = m_Database.RetrieveGroups(search); + + if (data != null && data.Length > 0) + { + foreach (GroupData d in data) + { + // Don't list group proxies + if (d.Data.ContainsKey("Location") && d.Data["Location"] != string.Empty) + continue; + + DirGroupsReplyData g = new DirGroupsReplyData(); + g.groupID = d.GroupID; + + if (d.Data.ContainsKey("Name")) + g.groupName = d.Data["Name"]; + else + m_log.DebugFormat("[Groups]: Key Name not found"); + + g.members = m_Database.MemberCount(d.GroupID); + + groups.Add(g); + } + } + + return groups; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List members = new List(); + + GroupData group = m_Database.RetrieveGroup(GroupID); + if (group == null) + return members; + + UUID ownerRoleID = new UUID(group.Data["OwnerRoleID"]); + + RoleData[] roles = m_Database.RetrieveRoles(GroupID); + if (roles == null) + // something wrong with this group + return members; + List rolesList = new List(roles); + + // Is the requester a member of the group? + bool isInGroup = false; + if (m_Database.RetrieveMember(GroupID, RequestingAgentID) != null) + isInGroup = true; + + if (!isInGroup) // reduce the roles to the visible ones + rolesList = rolesList.FindAll(r => (UInt64.Parse(r.Data["Powers"]) & (ulong)GroupPowers.MemberVisible) != 0); + + MembershipData[] datas = m_Database.RetrieveMembers(GroupID); + if (datas == null || (datas != null && datas.Length == 0)) + return members; + + // OK, we have everything we need + + foreach (MembershipData d in datas) + { + RoleMembershipData[] rolememberships = m_Database.RetrieveMemberRoles(GroupID, d.PrincipalID); + List rolemembershipsList = new List(rolememberships); + + ExtendedGroupMembersData m = new ExtendedGroupMembersData(); + + // What's this person's current role in the group? + UUID selectedRole = new UUID(d.Data["SelectedRoleID"]); + RoleData selected = rolesList.Find(r => r.RoleID == selectedRole); + + if (selected != null) + { + m.Title = selected.Data["Title"]; + m.AgentPowers = UInt64.Parse(selected.Data["Powers"]); + + m.AgentID = d.PrincipalID; + m.AcceptNotices = d.Data["AcceptNotices"] == "1" ? true : false; + m.Contribution = Int32.Parse(d.Data["Contribution"]); + m.ListInProfile = d.Data["ListInProfile"] == "1" ? true : false; + + // Is this person an owner of the group? + m.IsOwner = (rolemembershipsList.Find(r => r.RoleID == ownerRoleID) != null) ? true : false; + + members.Add(m); + } + } + + return members; + } + + public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason) + { + reason = string.Empty; + // check that the requesting agent has permissions to add role + if (!HasPower(RequestingAgentID, groupID, GroupPowers.CreateRole)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at creating role in group {1} denied because of lack of permission", RequestingAgentID, groupID); + reason = "Insufficient permission to create role"; + return false; + } + + return _AddOrUpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, true); + + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + // check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.ChangeActions)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at changing role in group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + return _AddOrUpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, false); + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + // check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.DeleteRole)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at deleting role from group {1} denied because of lack of permission", RequestingAgentID, groupID); + return; + } + + // Can't delete Everyone and Owners roles + if (roleID == UUID.Zero) + { + m_log.DebugFormat("[Groups]: Attempt at deleting Everyone role from group {0} denied", groupID); + return; + } + + GroupData group = m_Database.RetrieveGroup(groupID); + if (group == null) + { + m_log.DebugFormat("[Groups]: Attempt at deleting role from non-existing group {0}", groupID); + return; + } + + if (roleID == new UUID(group.Data["OwnerRoleID"])) + { + m_log.DebugFormat("[Groups]: Attempt at deleting Owners role from group {0} denied", groupID); + return; + } + + _RemoveGroupRole(groupID, roleID); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + // TODO: check perms + return _GetGroupRoles(GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + // TODO: check perms + + // Is the requester a member of the group? + bool isInGroup = false; + if (m_Database.RetrieveMember(GroupID, RequestingAgentID) != null) + isInGroup = true; + + return _GetGroupRoleMembers(GroupID, isInGroup); + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + reason = string.Empty; + + _AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, token); + + return true; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // check perms + if (RequestingAgentID != AgentID && !HasPower(RequestingAgentID, GroupID, GroupPowers.Eject)) + return; + + _RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID) + { + // Check whether the invitee is already a member of the group + MembershipData m = m_Database.RetrieveMember(groupID, agentID); + if (m != null) + return false; + + // Check permission to invite + if (!HasPower(RequestingAgentID, groupID, GroupPowers.Invite)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at inviting to group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + // Check whether there are pending invitations and delete them + InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID); + if (invite != null) + m_Database.DeleteInvite(invite.InviteID); + + invite = new InvitationData(); + invite.InviteID = inviteID; + invite.PrincipalID = agentID; + invite.GroupID = groupID; + invite.RoleID = roleID; + invite.Data = new Dictionary(); + + return m_Database.StoreInvitation(invite); + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + InvitationData data = m_Database.RetrieveInvitation(inviteID); + + if (data == null) + return null; + + GroupInviteInfo inviteInfo = new GroupInviteInfo(); + inviteInfo.AgentID = data.PrincipalID; + inviteInfo.GroupID = data.GroupID; + inviteInfo.InviteID = data.InviteID; + inviteInfo.RoleID = data.RoleID; + + return inviteInfo; + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_Database.DeleteInvite(inviteID); + } + + public bool AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + //if (!m_Database.CheckOwnerRole(RequestingAgentID, GroupID, RoleID)) + // return; + + // check permissions + bool limited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMemberLimited); + bool unlimited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMember) | IsOwner(RequestingAgentID, GroupID); + if (!limited || !unlimited) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at assigning {1} to role {2} denied because of lack of permission", RequestingAgentID, AgentID, RoleID); + return false; + } + + // AssignMemberLimited means that the person can assign another person to the same roles that she has in the group + if (!unlimited && limited) + { + // check whether person's has this role + RoleMembershipData rolemembership = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + if (rolemembership == null) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at assigning {1} to role {2} denied because of limited permission", RequestingAgentID, AgentID, RoleID); + return false; + } + } + + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + + return true; + } + + public bool RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + // Don't remove from Everyone role! + if (RoleID == UUID.Zero) + return false; + + // check permissions + bool unlimited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMember) || IsOwner(RequestingAgentID, GroupID); + if (!unlimited) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at removing {1} from role {2} denied because of lack of permission", RequestingAgentID, AgentID, RoleID); + return false; + } + + RoleMembershipData rolemember = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + + if (rolemember == null) + return false; + + m_Database.DeleteRoleMember(rolemember); + + // Find another role for this person + UUID newRoleID = UUID.Zero; // Everyone + RoleMembershipData[] rdata = m_Database.RetrieveMemberRoles(GroupID, AgentID); + if (rdata != null) + foreach (RoleMembershipData r in rdata) + { + if (r.RoleID != UUID.Zero) + { + newRoleID = r.RoleID; + break; + } + } + + MembershipData member = m_Database.RetrieveMember(GroupID, AgentID); + if (member != null) + { + member.Data["SelectedRoleID"] = newRoleID.ToString(); + m_Database.StoreMember(member); + } + + return true; + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + List roles = new List(); + // TODO: check permissions + + RoleMembershipData[] data = m_Database.RetrieveMemberRoles(GroupID, AgentID); + if (data == null || (data != null && data.Length ==0)) + return roles; + + foreach (RoleMembershipData d in data) + { + RoleData rdata = m_Database.RetrieveRole(GroupID, d.RoleID); + if (rdata == null) // hippos + continue; + + GroupRolesData r = new GroupRolesData(); + r.Name = rdata.Data["Name"]; + r.Powers = UInt64.Parse(rdata.Data["Powers"]); + r.RoleID = rdata.RoleID; + r.Title = rdata.Data["Title"]; + + roles.Add(r); + } + + return roles; + } + + public ExtendedGroupMembershipData SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // TODO: check perms + PrincipalData principal = new PrincipalData(); + principal.PrincipalID = AgentID; + principal.ActiveGroupID = GroupID; + m_Database.StorePrincipal(principal); + + return GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + // 1. get the principal data for the active group + PrincipalData principal = m_Database.RetrievePrincipal(AgentID); + if (principal == null) + return null; + + return GetAgentGroupMembership(RequestingAgentID, AgentID, principal.ActiveGroupID); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + return GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID, null); + } + + private ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID, MembershipData membership) + { + // 2. get the active group + GroupData group = m_Database.RetrieveGroup(GroupID); + if (group == null) + return null; + + // 3. get the membership info if we don't have it already + if (membership == null) + { + membership = m_Database.RetrieveMember(group.GroupID, AgentID); + if (membership == null) + return null; + } + + // 4. get the active role + UUID activeRoleID = new UUID(membership.Data["SelectedRoleID"]); + RoleData role = m_Database.RetrieveRole(group.GroupID, activeRoleID); + + ExtendedGroupMembershipData data = new ExtendedGroupMembershipData(); + data.AcceptNotices = membership.Data["AcceptNotices"] == "1" ? true : false; + data.AccessToken = membership.Data["AccessToken"]; + data.Active = true; + data.ActiveRole = activeRoleID; + data.AllowPublish = group.Data["AllowPublish"] == "1" ? true : false; + data.Charter = group.Data["Charter"]; + data.Contribution = Int32.Parse(membership.Data["Contribution"]); + data.FounderID = new UUID(group.Data["FounderID"]); + data.GroupID = new UUID(group.GroupID); + data.GroupName = group.Data["Name"]; + data.GroupPicture = new UUID(group.Data["InsigniaID"]); + if (role != null) + { + data.GroupPowers = UInt64.Parse(role.Data["Powers"]); + data.GroupTitle = role.Data["Title"]; + } + data.ListInProfile = membership.Data["ListInProfile"] == "1" ? true : false; + data.MaturePublish = group.Data["MaturePublish"] == "1" ? true : false; + data.MembershipFee = Int32.Parse(group.Data["MembershipFee"]); + data.OpenEnrollment = group.Data["OpenEnrollment"] == "1" ? true : false; + data.ShowInList = group.Data["ShowInList"] == "1" ? true : false; + + return data; + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + List memberships = new List(); + + // 1. Get all the groups that this person is a member of + MembershipData[] mdata = m_Database.RetrieveMemberships(AgentID); + + if (mdata == null || (mdata != null && mdata.Length == 0)) + return memberships; + + foreach (MembershipData d in mdata) + { + GroupMembershipData gmember = GetAgentGroupMembership(RequestingAgentID, AgentID, d.GroupID, d); + if (gmember != null) + { + memberships.Add(gmember); + //m_log.DebugFormat("[XXX]: Member of {0} as {1}", gmember.GroupName, gmember.GroupTitle); + //Util.PrintCallStack(); + } + } + + return memberships; + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + MembershipData data = m_Database.RetrieveMember(GroupID, AgentID); + if (data == null) + return; + + data.Data["SelectedRoleID"] = RoleID.ToString(); + m_Database.StoreMember(data); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + // TODO: check perms + + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership == null) + return; + + membership.Data["AcceptNotices"] = AcceptNotices ? "1" : "0"; + membership.Data["ListInProfile"] = ListInProfile ? "1" : "0"; + + m_Database.StoreMember(membership); + } + + public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + // Check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.SendNotices)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at sending notice to group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID); + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + NoticeData data = m_Database.RetrieveNotice(noticeID); + + if (data == null) + return null; + + return _NoticeDataToInfo(data); + } + + public List GetGroupNotices(string RequestingAgentID, UUID groupID) + { + NoticeData[] data = m_Database.RetrieveNotices(groupID); + List infos = new List(); + + if (data == null || (data != null && data.Length == 0)) + return infos; + + foreach (NoticeData d in data) + { + ExtendedGroupNoticeData info = _NoticeDataToData(d); + infos.Add(info); + } + + return infos; + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #region Actions without permission checks + + private void _AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + _AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, string.Empty); + } + + public void _RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // 1. Delete membership + m_Database.DeleteMember(GroupID, AgentID); + + // 2. Remove from rolememberships + m_Database.DeleteMemberAllRoles(GroupID, AgentID); + + // 3. if it was active group, inactivate it + PrincipalData principal = m_Database.RetrievePrincipal(AgentID); + if (principal != null && principal.ActiveGroupID == GroupID) + { + principal.ActiveGroupID = UUID.Zero; + m_Database.StorePrincipal(principal); + } + } + + protected void _AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string accessToken) + { + // Check if it's already there + MembershipData data = m_Database.RetrieveMember(GroupID, AgentID); + if (data != null) + return; + + // Add the membership + data = new MembershipData(); + data.PrincipalID = AgentID; + data.GroupID = GroupID; + data.Data = new Dictionary(); + data.Data["SelectedRoleID"] = RoleID.ToString(); + data.Data["Contribution"] = "0"; + data.Data["ListInProfile"] = "1"; + data.Data["AcceptNotices"] = "1"; + data.Data["AccessToken"] = accessToken; + + m_Database.StoreMember(data); + + // Add principal to everyone role + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, UUID.Zero); + + // Add principal to role, if different from everyone role + if (RoleID != UUID.Zero) + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + + // Make thit this active group + PrincipalData pdata = new PrincipalData(); + pdata.PrincipalID = AgentID; + pdata.ActiveGroupID = GroupID; + m_Database.StorePrincipal(pdata); + + } + + private bool _AddOrUpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, bool add) + { + RoleData data = m_Database.RetrieveRole(groupID, roleID); + + if (add && data != null) // it already exists, can't create + return false; + + if (!add && data == null) // it deosn't exist, can't update + return false; + + if (add) + data = new RoleData(); + + data.GroupID = groupID; + data.RoleID = roleID; + data.Data = new Dictionary(); + data.Data["Name"] = name; + data.Data["Description"] = description; + data.Data["Title"] = title; + data.Data["Powers"] = powers.ToString(); + + return m_Database.StoreRole(data); + } + + private void _RemoveGroupRole(UUID groupID, UUID roleID) + { + m_Database.DeleteRole(groupID, roleID); + } + + private void _AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + RoleMembershipData data = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + if (data != null) + return; + + data = new RoleMembershipData(); + data.GroupID = GroupID; + data.PrincipalID = AgentID; + data.RoleID = RoleID; + m_Database.StoreRoleMember(data); + + // Make it the SelectedRoleID + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership == null) + { + m_log.DebugFormat("[Groups]: ({0}) No such member {0} in group {1}", AgentID, GroupID); + return; + } + + membership.Data["SelectedRoleID"] = RoleID.ToString(); + m_Database.StoreMember(membership); + + } + + private List _GetGroupRoles(UUID groupID) + { + List roles = new List(); + + RoleData[] data = m_Database.RetrieveRoles(groupID); + + if (data == null || (data != null && data.Length == 0)) + return roles; + + foreach (RoleData d in data) + { + GroupRolesData r = new GroupRolesData(); + r.Description = d.Data["Description"]; + r.Members = m_Database.RoleMemberCount(groupID, d.RoleID); + r.Name = d.Data["Name"]; + r.Powers = UInt64.Parse(d.Data["Powers"]); + r.RoleID = d.RoleID; + r.Title = d.Data["Title"]; + + roles.Add(r); + } + + return roles; + } + + private List _GetGroupRoleMembers(UUID GroupID, bool isInGroup) + { + List rmembers = new List(); + + RoleData[] rdata = new RoleData[0]; + if (!isInGroup) + { + rdata = m_Database.RetrieveRoles(GroupID); + if (rdata == null || (rdata != null && rdata.Length == 0)) + return rmembers; + } + List rlist = new List(rdata); + if (!isInGroup) + rlist = rlist.FindAll(r => (UInt64.Parse(r.Data["Powers"]) & (ulong)GroupPowers.MemberVisible) != 0); + + RoleMembershipData[] data = m_Database.RetrieveRolesMembers(GroupID); + + if (data == null || (data != null && data.Length == 0)) + return rmembers; + + foreach (RoleMembershipData d in data) + { + if (!isInGroup) + { + RoleData rd = rlist.Find(_r => _r.RoleID == d.RoleID); // visible role + if (rd == null) + continue; + } + + ExtendedGroupRoleMembersData r = new ExtendedGroupRoleMembersData(); + r.MemberID = d.PrincipalID; + r.RoleID = d.RoleID; + + rmembers.Add(r); + } + + return rmembers; + } + + protected bool _AddNotice(UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + NoticeData data = new NoticeData(); + data.GroupID = groupID; + data.NoticeID = noticeID; + data.Data = new Dictionary(); + data.Data["FromName"] = fromName; + data.Data["Subject"] = subject; + data.Data["Message"] = message; + data.Data["HasAttachment"] = hasAttachment ? "1" : "0"; + if (hasAttachment) + { + data.Data["AttachmentType"] = attType.ToString(); + data.Data["AttachmentName"] = attName; + data.Data["AttachmentItemID"] = attItemID.ToString(); + data.Data["AttachmentOwnerID"] = attOwnerID; + } + data.Data["TMStamp"] = ((uint)Util.UnixTimeSinceEpoch()).ToString(); + + return m_Database.StoreNotice(data); + } + + #endregion + + #region structure translations + ExtendedGroupRecord _GroupDataToRecord(GroupData data) + { + if (data == null) + return null; + + ExtendedGroupRecord rec = new ExtendedGroupRecord(); + rec.AllowPublish = data.Data["AllowPublish"] == "1" ? true : false; + rec.Charter = data.Data["Charter"]; + rec.FounderID = new UUID(data.Data["FounderID"]); + rec.GroupID = data.GroupID; + rec.GroupName = data.Data["Name"]; + rec.GroupPicture = new UUID(data.Data["InsigniaID"]); + rec.MaturePublish = data.Data["MaturePublish"] == "1" ? true : false; + rec.MembershipFee = Int32.Parse(data.Data["MembershipFee"]); + rec.OpenEnrollment = data.Data["OpenEnrollment"] == "1" ? true : false; + rec.OwnerRoleID = new UUID(data.Data["OwnerRoleID"]); + rec.ShowInList = data.Data["ShowInList"] == "1" ? true : false; + rec.ServiceLocation = data.Data["Location"]; + rec.MemberCount = m_Database.MemberCount(data.GroupID); + rec.RoleCount = m_Database.RoleCount(data.GroupID); + + return rec; + } + + GroupNoticeInfo _NoticeDataToInfo(NoticeData data) + { + GroupNoticeInfo notice = new GroupNoticeInfo(); + notice.GroupID = data.GroupID; + notice.Message = data.Data["Message"]; + notice.noticeData = _NoticeDataToData(data); + + return notice; + } + + ExtendedGroupNoticeData _NoticeDataToData(NoticeData data) + { + ExtendedGroupNoticeData notice = new ExtendedGroupNoticeData(); + notice.FromName = data.Data["FromName"]; + notice.NoticeID = data.NoticeID; + notice.Subject = data.Data["Subject"]; + notice.Timestamp = uint.Parse((string)data.Data["TMStamp"]); + notice.HasAttachment = data.Data["HasAttachment"] == "1" ? true : false; + if (notice.HasAttachment) + { + notice.AttachmentName = data.Data["AttachmentName"]; + notice.AttachmentItemID = new UUID(data.Data["AttachmentItemID"].ToString()); + notice.AttachmentType = byte.Parse(data.Data["AttachmentType"].ToString()); + notice.AttachmentOwnerID = data.Data["AttachmentOwnerID"].ToString(); + } + + + return notice; + } + + #endregion + + #region permissions + private bool HasPower(string agentID, UUID groupID, GroupPowers power) + { + RoleMembershipData[] rmembership = m_Database.RetrieveMemberRoles(groupID, agentID); + if (rmembership == null || (rmembership != null && rmembership.Length == 0)) + return false; + + foreach (RoleMembershipData rdata in rmembership) + { + RoleData role = m_Database.RetrieveRole(groupID, rdata.RoleID); + if ( (UInt64.Parse(role.Data["Powers"]) & (ulong)power) != 0 ) + return true; + } + return false; + } + + private bool IsOwner(string agentID, UUID groupID) + { + GroupData group = m_Database.RetrieveGroup(groupID); + if (group == null) + return false; + + RoleMembershipData rmembership = m_Database.RetrieveRoleMember(groupID, new UUID(group.Data["OwnerRoleID"]), agentID); + if (rmembership == null) + return false; + + return true; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs b/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs new file mode 100644 index 0000000..2611a3d --- /dev/null +++ b/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs @@ -0,0 +1,84 @@ +/* + * 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.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.Groups +{ + public class GroupsServiceBase : ServiceBase + { + protected IGroupsData m_Database = null; + + public GroupsServiceBase(IConfigSource config, string cName) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "os_groups"; + string configName = (cName == string.Empty) ? "Groups" : cName; + + // + // Try reading the [DatabaseService] section, if it exists + // + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + if (dllName == String.Empty) + dllName = dbConfig.GetString("StorageProvider", String.Empty); + if (connString == String.Empty) + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + // + // [Groups] section overrides [DatabaseService], if it exists + // + IConfig groupsConfig = config.Configs[configName]; + if (groupsConfig != null) + { + dllName = groupsConfig.GetString("StorageProvider", dllName); + connString = groupsConfig.GetString("ConnectionString", connString); + realm = groupsConfig.GetString("Realm", realm); + } + + // + // We tried, but this doesn't exist. We can't proceed. + // + if (dllName.Equals(String.Empty)) + throw new Exception("No StorageProvider configured"); + + m_Database = LoadPlugin(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module " + dllName); + } + } +} \ No newline at end of file diff --git a/OpenSim/Addons/Groups/Service/HGGroupsService.cs b/OpenSim/Addons/Groups/Service/HGGroupsService.cs new file mode 100644 index 0000000..9d7961c --- /dev/null +++ b/OpenSim/Addons/Groups/Service/HGGroupsService.cs @@ -0,0 +1,353 @@ +/* + * 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.Reflection; +using System.Timers; +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Groups +{ + public class HGGroupsService : GroupsService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IOfflineIMService m_OfflineIM; + private IUserAccountService m_UserAccounts; + private string m_HomeURI; + + public HGGroupsService(IConfigSource config, IOfflineIMService im, IUserAccountService users, string homeURI) + : base(config, string.Empty) + { + m_OfflineIM = im; + m_UserAccounts = users; + m_HomeURI = homeURI; + if (!m_HomeURI.EndsWith("/")) + m_HomeURI += "/"; + } + + + #region HG specific operations + + public bool CreateGroupProxy(string RequestingAgentID, string agentID, string accessToken, UUID groupID, string serviceLocation, string name, out string reason) + { + reason = string.Empty; + Uri uri = null; + try + { + uri = new Uri(serviceLocation); + } + catch (UriFormatException) + { + reason = "Bad location for group proxy"; + return false; + } + + // Check if it already exists + GroupData grec = m_Database.RetrieveGroup(groupID); + if (grec == null || + (grec != null && grec.Data["Location"] != string.Empty && grec.Data["Location"].ToLower() != serviceLocation.ToLower())) + { + // Create the group + grec = new GroupData(); + grec.GroupID = groupID; + grec.Data = new Dictionary(); + grec.Data["Name"] = name + " @ " + uri.Authority; + grec.Data["Location"] = serviceLocation; + grec.Data["Charter"] = string.Empty; + grec.Data["InsigniaID"] = UUID.Zero.ToString(); + grec.Data["FounderID"] = UUID.Zero.ToString(); + grec.Data["MembershipFee"] = "0"; + grec.Data["OpenEnrollment"] = "0"; + grec.Data["ShowInList"] = "0"; + grec.Data["AllowPublish"] = "0"; + grec.Data["MaturePublish"] = "0"; + grec.Data["OwnerRoleID"] = UUID.Zero.ToString(); + + + if (!m_Database.StoreGroup(grec)) + return false; + } + + if (grec.Data["Location"] == string.Empty) + { + reason = "Cannot add proxy membership to non-proxy group"; + return false; + } + + UUID uid = UUID.Zero; + string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty; + Util.ParseUniversalUserIdentifier(RequestingAgentID, out uid, out url, out first, out last, out tmp); + string fromName = first + "." + last + "@" + url; + + // Invite to group again + InviteToGroup(fromName, groupID, new UUID(agentID), grec.Data["Name"]); + + // Stick the proxy membership in the DB already + // we'll delete it if the agent declines the invitation + MembershipData membership = new MembershipData(); + membership.PrincipalID = agentID; + membership.GroupID = groupID; + membership.Data = new Dictionary(); + membership.Data["SelectedRoleID"] = UUID.Zero.ToString(); + membership.Data["Contribution"] = "0"; + membership.Data["ListInProfile"] = "1"; + membership.Data["AcceptNotices"] = "1"; + membership.Data["AccessToken"] = accessToken; + + m_Database.StoreMember(membership); + + return true; + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, string token) + { + // check the token + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership != null) + { + if (token != string.Empty && token.Equals(membership.Data["AccessToken"])) + RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + else + m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]); + } + else + m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", AgentID); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token) + { + // check the token + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return null; + + ExtendedGroupRecord grec; + if (GroupID == UUID.Zero) + grec = GetGroupRecord(RequestingAgentID, groupName); + else + grec = GetGroupRecord(RequestingAgentID, GroupID); + + if (grec != null) + FillFounderUUI(grec); + + return grec; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + List members = GetGroupMembers(RequestingAgentID, GroupID); + + // convert UUIDs to UUIs + members.ForEach(delegate (ExtendedGroupMembersData m) + { + if (m.AgentID.ToString().Length == 36) // UUID + { + UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.AgentID)); + if (account != null) + m.AgentID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI); + } + }); + + return members; + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + return GetGroupRoles(RequestingAgentID, GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + List rolemembers = GetGroupRoleMembers(RequestingAgentID, GroupID); + + // convert UUIDs to UUIs + rolemembers.ForEach(delegate(ExtendedGroupRoleMembersData m) + { + if (m.MemberID.ToString().Length == 36) // UUID + { + UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.MemberID)); + if (account != null) + m.MemberID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI); + } + }); + + return rolemembers; + } + + public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + // check that the group proxy exists + ExtendedGroupRecord grec = GetGroupRecord(RequestingAgentID, groupID); + if (grec == null) + { + m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to non-existent group proxy"); + return false; + } + + // check that the group is remote + if (grec.ServiceLocation == string.Empty) + { + m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to local (non-proxy) group"); + return false; + } + + // check that there isn't already a notice with the same ID + if (GetGroupNotice(RequestingAgentID, noticeID) != null) + { + m_log.DebugFormat("[Groups.HGGroupsService]: a notice with the same ID already exists", grec.ServiceLocation); + return false; + } + + // This has good intentions (security) but it will potentially DDS the origin... + // We'll need to send a proof along with the message. Maybe encrypt the message + // using key pairs + // + //// check that the notice actually exists in the origin + //GroupsServiceHGConnector c = new GroupsServiceHGConnector(grec.ServiceLocation); + //if (!c.VerifyNotice(noticeID, groupID)) + //{ + // m_log.DebugFormat("[Groups.HGGroupsService]: notice does not exist at origin {0}", grec.ServiceLocation); + // return false; + //} + + // ok, we're good! + return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID); + } + + public bool VerifyNotice(UUID noticeID, UUID groupID) + { + GroupNoticeInfo notice = GetGroupNotice(string.Empty, noticeID); + + if (notice == null) + return false; + + if (notice.GroupID != groupID) + return false; + + return true; + } + + #endregion + + private void InviteToGroup(string fromName, UUID groupID, UUID invitedAgentID, string groupName) + { + // Todo: Security check, probably also want to send some kind of notification + UUID InviteID = UUID.Random(); + + if (AddAgentToGroupInvite(InviteID, groupID, invitedAgentID.ToString())) + { + Guid inviteUUID = InviteID.Guid; + + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = inviteUUID; + + // msg.fromAgentID = agentID.Guid; + msg.fromAgentID = groupID.Guid; + msg.toAgentID = invitedAgentID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = fromName; + msg.message = string.Format("Please confirm your acceptance to join group {0}.", groupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + msg.binaryBucket = new byte[20]; + + string reason = string.Empty; + m_OfflineIM.StoreMessage(msg, out reason); + + } + } + + private bool AddAgentToGroupInvite(UUID inviteID, UUID groupID, string agentID) + { + // Check whether the invitee is already a member of the group + MembershipData m = m_Database.RetrieveMember(groupID, agentID); + if (m != null) + return false; + + // Check whether there are pending invitations and delete them + InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID); + if (invite != null) + m_Database.DeleteInvite(invite.InviteID); + + invite = new InvitationData(); + invite.InviteID = inviteID; + invite.PrincipalID = agentID; + invite.GroupID = groupID; + invite.RoleID = UUID.Zero; + invite.Data = new Dictionary(); + + return m_Database.StoreInvitation(invite); + } + + private void FillFounderUUI(ExtendedGroupRecord grec) + { + UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, grec.FounderID); + if (account != null) + grec.FounderUUI = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI); + } + + private bool VerifyToken(UUID groupID, string agentID, string token) + { + // check the token + MembershipData membership = m_Database.RetrieveMember(groupID, agentID); + if (membership != null) + { + if (token != string.Empty && token.Equals(membership.Data["AccessToken"])) + return true; + else + m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]); + } + else + m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", agentID); + + return false; + } + } +} -- cgit v1.1