diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupDataProvider.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupDataProvider.cs new file mode 100644 index 0000000000..6e9105d1c6 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupDataProvider.cs @@ -0,0 +1,85 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; + +using OpenMetaverse; + +using OpenSim.Framework; + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + interface IGroupDataProvider + { + UUID CreateGroup(string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID); + void UpdateGroup(UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish); + GroupRecord GetGroupRecord(UUID GroupID, string GroupName); + List FindGroups(string search); + List GetGroupMembers(UUID GroupID); + + void AddGroupRole(UUID groupID, UUID roleID, string name, string description, string title, ulong powers); + void UpdateGroupRole(UUID groupID, UUID roleID, string name, string description, string title, ulong powers); + void RemoveGroupRole(UUID groupID, UUID roleID); + List GetGroupRoles(UUID GroupID); + List GetGroupRoleMembers(UUID GroupID); + + void AddAgentToGroup(UUID AgentID, UUID GroupID, UUID RoleID); + void RemoveAgentFromGroup(UUID AgentID, UUID GroupID); + + void AddAgentToGroupInvite(UUID inviteID, UUID groupID, UUID roleID, UUID agentID); + GroupInviteInfo GetAgentToGroupInvite(UUID inviteID); + void RemoveAgentToGroupInvite(UUID inviteID); + + + void AddAgentToGroupRole(UUID AgentID, UUID GroupID, UUID RoleID); + void RemoveAgentFromGroupRole(UUID AgentID, UUID GroupID, UUID RoleID); + List GetAgentGroupRoles(UUID AgentID, UUID GroupID); + + void SetAgentActiveGroup(UUID AgentID, UUID GroupID); + GroupMembershipData GetAgentActiveMembership(UUID AgentID); + + void SetAgentActiveGroupRole(UUID AgentID, UUID GroupID, UUID RoleID); + void SetAgentGroupInfo(UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile); + + GroupMembershipData GetAgentGroupMembership(UUID AgentID, UUID GroupID); + List GetAgentGroupMemberships(UUID AgentID); + + void AddGroupNotice(UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket); + GroupNoticeInfo GetGroupNotice(UUID noticeID); + List GetGroupNotices(UUID GroupID); + } + + public class GroupInviteInfo + { + public UUID GroupID = UUID.Zero; + public UUID RoleID = UUID.Zero; + public UUID AgentID = UUID.Zero; + public UUID InviteID = UUID.Zero; + } + +} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs new file mode 100644 index 0000000000..343bd6df23 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs @@ -0,0 +1,1017 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +//using System.Text; + +using Nwc.XmlRpc; + +using log4net; +// using Nini.Config; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +//using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + + public class XmlRpcGroupDataProvider : IGroupDataProvider + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serviceURL = "http://osflotsam.org/xmlrpc.php"; + + public const GroupPowers m_DefaultEveryonePowers = GroupPowers.AllowSetHome | GroupPowers.Accountable | GroupPowers.JoinChat | GroupPowers.AllowVoiceChat | GroupPowers.ReceiveNotices | GroupPowers.StartProposal | GroupPowers.VoteOnProposal; + + public XmlRpcGroupDataProvider(string serviceURL) + { + m_serviceURL = serviceURL; + } + + /// + /// Create a Group, including Everyone and Owners Role, place FounderID in both groups, select Owner as selected role, and newly created group as agent's active role. + /// + public UUID CreateGroup(string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID) + { + UUID GroupID = UUID.Random(); + UUID OwnerRoleID = UUID.Random(); + + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + param["Name"] = name; + param["Charter"] = charter; + param["ShowInList"] = showInList == true ? 1 : 0; + param["InsigniaID"] = insigniaID.ToString(); + param["MembershipFee"] = 0; + param["OpenEnrollment"] = openEnrollment == true ? 1 : 0; + param["AllowPublish"] = allowPublish == true ? 1 : 0; + param["MaturePublish"] = maturePublish == true ? 1 : 0; + param["FounderID"] = founderID.ToString(); + param["EveryonePowers"] = ((ulong)m_DefaultEveryonePowers).ToString(); + param["OwnerRoleID"] = OwnerRoleID.ToString(); + + // Would this be cleaner as (GroupPowers)ulong.MaxValue; + 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; + param["OwnersPowers"] = ((ulong)OwnerPowers).ToString(); + + + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.createGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + + return UUID.Parse((string)respData["GroupID"]); + } + + public void UpdateGroup(UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["Charter"] = charter; + param["ShowInList"] = showInList == true ? 1 : 0; + param["InsigniaID"] = insigniaID.ToString(); + param["MembershipFee"] = membershipFee; + param["OpenEnrollment"] = openEnrollment == true ? 1 : 0; + param["AllowPublish"] = allowPublish == true ? 1 : 0; + param["MaturePublish"] = maturePublish == true ? 1 : 0; + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.updateGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void AddGroupRole(UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["RoleID"] = roleID.ToString(); + param["Name"] = name; + param["Description"] = description; + param["Title"] = title; + param["Powers"] = powers.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.addRoleToGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void RemoveGroupRole(UUID groupID, UUID roleID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["RoleID"] = roleID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.removeRoleFromGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void UpdateGroupRole(UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["RoleID"] = roleID.ToString(); + if (name != null) + { + param["Name"] = name; + } + if (description != null) + { + param["Description"] = description; + } + if (title != null) + { + param["Title"] = title; + } + param["Powers"] = powers.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.updateGroupRole", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public GroupRecord GetGroupRecord(UUID GroupID, string GroupName) + { + Hashtable param = new Hashtable(); + if ((GroupID != null) && (GroupID != UUID.Zero)) + { + param["GroupID"] = GroupID.ToString(); + } + if ((GroupName != null) && (GroupName != string.Empty)) + { + param["Name"] = GroupName.ToString(); + } + + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + if ((string)respData["error"] != "Group Not Found") + { + LogRespDataToConsoleError(respData); + } + return null; + } + + return GroupProfileHashtableToGroupRecord(respData); + + } + + public GroupProfileData GetMemberGroupProfile(UUID GroupID, UUID AgentID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + if ((string)respData["error"] != "Group Not Found") + { + LogRespDataToConsoleError(respData); + } + return new GroupProfileData(); + } + + GroupMembershipData MemberInfo = GetAgentGroupMembership(AgentID, GroupID); + GroupProfileData MemberGroupProfile = GroupProfileHashtableToGroupProfileData(respData); + + MemberGroupProfile.MemberTitle = MemberInfo.GroupTitle; + MemberGroupProfile.PowersMask = MemberInfo.GroupPowers; + + return MemberGroupProfile; + + } + + private GroupProfileData GroupProfileHashtableToGroupProfileData(Hashtable groupProfile) + { + GroupProfileData group = new GroupProfileData(); + group.GroupID = UUID.Parse((string)groupProfile["GroupID"]); + group.Name = (string)groupProfile["Name"]; + + if (groupProfile["Charter"] != null) + { + group.Charter = (string)groupProfile["Charter"]; + } + + group.ShowInList = ((string)groupProfile["ShowInList"]) == "1"; + group.InsigniaID = UUID.Parse((string)groupProfile["InsigniaID"]); + group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]); + group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1"; + group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1"; + group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1"; + group.FounderID = UUID.Parse((string)groupProfile["FounderID"]); + group.OwnerRole = UUID.Parse((string)groupProfile["OwnerRoleID"]); + + group.GroupMembershipCount = int.Parse((string)groupProfile["GroupMembershipCount"]); + group.GroupRolesCount = int.Parse((string)groupProfile["GroupRolesCount"]); + + return group; + } + + private GroupRecord GroupProfileHashtableToGroupRecord(Hashtable groupProfile) + { + + GroupRecord group = new GroupRecord(); + m_log.Debug("GroupID"); + group.GroupID = UUID.Parse((string)groupProfile["GroupID"]); + + m_log.Debug("Name"); + group.GroupName = groupProfile["Name"].ToString(); + + m_log.Debug("Charter"); + if (groupProfile["Charter"] != null) + { + group.Charter = (string)groupProfile["Charter"]; + } + + m_log.Debug("ShowInList"); + group.ShowInList = ((string)groupProfile["ShowInList"]) == "1"; + + m_log.Debug("InsigniaID"); + group.GroupPicture = UUID.Parse((string)groupProfile["InsigniaID"]); + + m_log.Debug("MembershipFee"); + group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]); + + m_log.Debug("OpenEnrollment"); + group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1"; + + m_log.Debug("AllowPublish"); + group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1"; + + m_log.Debug("MaturePublish"); + group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1"; + + m_log.Debug("FounderID"); + group.FounderID = UUID.Parse((string)groupProfile["FounderID"]); + + m_log.Debug("OwnerRoleID"); + group.OwnerRoleID = UUID.Parse((string)groupProfile["OwnerRoleID"]); + + return group; + } + + + public void SetAgentActiveGroup(UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.setAgentActiveGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + + } + + public void SetAgentActiveGroupRole(UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["SelectedRoleID"] = RoleID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.setAgentGroupInfo", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + + } + + public void SetAgentGroupInfo(UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["AcceptNotices"] = AcceptNotices ? "1" : "0"; + param["ListInProfile"] = ListInProfile ? "1" : "0"; + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.setAgentGroupInfo", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void AddAgentToGroupInvite(UUID inviteID, UUID groupID, UUID roleID, UUID agentID) + { + Hashtable param = new Hashtable(); + param["InviteID"] = inviteID.ToString(); + param["AgentID"] = agentID.ToString(); + param["RoleID"] = roleID.ToString(); + param["GroupID"] = groupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.addAgentToGroupInvite", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + if (respData["error"] != "Duplicate group invite requested") + { + LogRespDataToConsoleError(respData); + } + } + + + } + + public GroupInviteInfo GetAgentToGroupInvite(UUID inviteID) + { + Hashtable param = new Hashtable(); + param["InviteID"] = inviteID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getAgentToGroupInvite", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + + return null; + } + + GroupInviteInfo inviteInfo = new GroupInviteInfo(); + inviteInfo.InviteID = inviteID; + inviteInfo.GroupID = UUID.Parse((string)respData["GroupID"]); + inviteInfo.RoleID = UUID.Parse((string)respData["RoleID"]); + inviteInfo.AgentID = UUID.Parse((string)respData["AgentID"]); + + return inviteInfo; + } + + public void RemoveAgentToGroupInvite(UUID inviteID) + { + Hashtable param = new Hashtable(); + param["InviteID"] = inviteID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.removeAgentToGroupInvite", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void AddAgentToGroup(UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["RoleID"] = RoleID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.addAgentToGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void RemoveAgentFromGroup(UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.removeAgentFromGroup", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void AddAgentToGroupRole(UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["RoleID"] = RoleID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.addAgentToGroupRole", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + public void RemoveAgentFromGroupRole(UUID AgentID, UUID GroupID, UUID RoleID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + param["RoleID"] = RoleID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.removeAgentFromGroupRole", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 3000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + + public List FindGroups(string search) + { + Hashtable param = new Hashtable(); + param["Search"] = search; + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.findGroups", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + List findings = new List(); + + if (respData.Contains("error")) + { + if (respData["error"].ToString() != "No groups found.") + { + LogRespDataToConsoleError(respData); + } + } + else + { + Hashtable results = (Hashtable)respData["results"]; + foreach (Hashtable groupFind in results.Values) + { + DirGroupsReplyData data = new DirGroupsReplyData(); + data.groupID = new UUID((string)groupFind["GroupID"]); ; + data.groupName = (string)groupFind["Name"]; + data.members = int.Parse((string)groupFind["Members"]); + // data.searchOrder = order; + + findings.Add(data); + } + } + + return findings; + } + + public GroupMembershipData GetAgentGroupMembership(UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getAgentGroupMembership", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + if ((string)respData["error"] != "None Found") + { + LogRespDataToConsoleError(respData); + } + return null; + } + + GroupMembershipData data = HashTableToGroupMembershipData(respData); + + return data; + } + + public GroupMembershipData GetAgentActiveMembership(UUID AgentID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getAgentActiveMembership", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + if (respData["error"].ToString() == "No Active Group Specified") + { + return null; + } + LogRespDataToConsoleError(respData); + return null; + } + + try + { + GroupMembershipData data = HashTableToGroupMembershipData(respData); + return data; + } + catch (System.Exception e) + { + LogRespDataToConsoleError(respData); + throw e; + } + } + + private void LogRespDataToConsoleError(Hashtable respData) + { + m_log.Error("[GROUPDATA] Error:"); + + foreach (string key in respData.Keys) + { + m_log.ErrorFormat("[GROUPDATA] Key: {0}", key); + + object o = respData[key]; + + string[] lines = respData[key].ToString().Split(new char[] { '\n' }); + foreach (string line in lines) + { + m_log.ErrorFormat("[GROUPDATA] {0}", line); + } + + } + } + + public List GetAgentGroupMemberships(UUID AgentID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getAgentGroupMemberships", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + List memberships = new List(); + + if (respData.Contains("error")) + { + if (respData["error"].ToString() != "No Memberships") + { + LogRespDataToConsoleError(respData); + } + } + else + { + foreach (object membership in respData.Values) + { + memberships.Add(HashTableToGroupMembershipData((Hashtable)membership)); + } + } + return memberships; + } + + public List GetAgentGroupRoles(UUID AgentID, UUID GroupID) + { + Hashtable param = new Hashtable(); + param["AgentID"] = AgentID.ToString(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getAgentRoles", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + List Roles = new List(); + + if (respData.Contains("error")) + { + if ((string)respData["error"] != "None found") + { + LogRespDataToConsoleError(respData); + } + return Roles; + } + + foreach (Hashtable role in respData.Values) + { + GroupRolesData data = new GroupRolesData(); + data.RoleID = new UUID((string)role["RoleID"]); + data.Name = (string)role["Name"]; + data.Description = (string)role["Description"]; + data.Powers = ulong.Parse((string)role["Powers"]); + data.Title = (string)role["Title"]; + + Roles.Add(data); + } + + return Roles; + + + } + + public List GetGroupRoles(UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getGroupRoles", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + return null; + } + + List Roles = new List(); + foreach (Hashtable role in respData.Values) + { + GroupRolesData data = new GroupRolesData(); + data.Description = (string)role["Description"]; + data.Members = int.Parse((string)role["Members"]); + data.Name = (string)role["Name"]; + data.Powers = ulong.Parse((string)role["Powers"]); + data.RoleID = new UUID((string)role["RoleID"]); + data.Title = (string)role["Title"]; + + Roles.Add(data); + } + + return Roles; + + } + + private static GroupMembershipData HashTableToGroupMembershipData(Hashtable respData) + { + GroupMembershipData data = new GroupMembershipData(); + data.AcceptNotices = ((string)respData["AcceptNotices"] == "1"); + data.Contribution = int.Parse((string)respData["Contribution"]); + data.ListInProfile = ((string)respData["ListInProfile"] == "1"); + + data.ActiveRole = new UUID((string)respData["SelectedRoleID"]); + data.GroupTitle = (string)respData["Title"]; + + data.GroupPowers = ulong.Parse((string)respData["GroupPowers"]); + + // Is this group the agent's active group + + data.GroupID = new UUID((string)respData["GroupID"]); + + UUID ActiveGroup = new UUID((string)respData["ActiveGroupID"]); + data.Active = data.GroupID.Equals(ActiveGroup); + + data.AllowPublish = ((string)respData["AllowPublish"] == "1"); + data.Charter = (string)respData["Charter"]; + data.FounderID = new UUID((string)respData["FounderID"]); + data.GroupID = new UUID((string)respData["GroupID"]); + data.GroupName = (string)respData["GroupName"]; + data.GroupPicture = new UUID((string)respData["InsigniaID"]); + data.MaturePublish = ((string)respData["MaturePublish"] == "1"); + data.MembershipFee = int.Parse((string)respData["MembershipFee"]); + data.OpenEnrollment = ((string)respData["OpenEnrollment"] == "1"); + data.ShowInList = ((string)respData["ShowInList"] == "1"); + return data; + } + + public List GetGroupMembers(UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getGroupMembers", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + return null; + } + + List members = new List(); + foreach (Hashtable membership in respData.Values) + { + GroupMembersData data = new GroupMembersData(); + + data.AcceptNotices = ((string)membership["AcceptNotices"]) == "1"; + data.AgentID = new UUID((string)membership["AgentID"]); + data.Contribution = int.Parse((string)membership["Contribution"]); + data.IsOwner = ((string)membership["IsOwner"]) == "1"; + data.ListInProfile = ((string)membership["ListInProfile"]) == "1"; + data.AgentPowers = ulong.Parse((string)membership["AgentPowers"]); + data.Title = (string)membership["Title"]; + + members.Add(data); + } + + return members; + + } + + public List GetGroupRoleMembers(UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getGroupRoleMembers", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + return null; + } + + List members = new List(); + foreach (Hashtable membership in respData.Values) + { + GroupRoleMembersData data = new GroupRoleMembersData(); + + data.MemberID = new UUID((string)membership["AgentID"]); + data.RoleID = new UUID((string)membership["RoleID"]); + + members.Add(data); + } + + return members; + } + + public List GetGroupNotices(UUID GroupID) + { + Hashtable param = new Hashtable(); + param["GroupID"] = GroupID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getGroupNotices", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + List values = new List(); + + if (respData.Contains("error")) + { + if ((string)respData["error"] != "No Notices") + { + LogRespDataToConsoleError(respData); + } + } + else + { + foreach (Hashtable value in respData.Values) + { + GroupNoticeData data = new GroupNoticeData(); + data.NoticeID = UUID.Parse((string)value["NoticeID"]); + data.Timestamp = uint.Parse((string)value["Timestamp"]); + data.FromName = (string)value["FromName"]; + data.Subject = (string)value["Subject"]; + data.HasAttachment = false; + data.AssetType = 0; + + values.Add(data); + } + } + return values; + + } + public GroupNoticeInfo GetGroupNotice(UUID noticeID) + { + Hashtable param = new Hashtable(); + param["NoticeID"] = noticeID.ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.getGroupNotice", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + + + if (respData.Contains("error")) + { + if ((string)respData["error"] != "Group Notice Not Found") + { + LogRespDataToConsoleError(respData); + return null; + } + } + + GroupNoticeInfo data = new GroupNoticeInfo(); + data.GroupID = UUID.Parse((string)respData["GroupID"]); + data.Message = (string)respData["Message"]; + data.BinaryBucket = Utils.HexStringToBytes((string)respData["BinaryBucket"], true); + data.noticeData.NoticeID = UUID.Parse((string)respData["NoticeID"]); + data.noticeData.Timestamp = uint.Parse((string)respData["Timestamp"]); + data.noticeData.FromName = (string)respData["FromName"]; + data.noticeData.Subject = (string)respData["Subject"]; + data.noticeData.HasAttachment = false; + data.noticeData.AssetType = 0; + + if (data.Message == null) + { + data.Message = string.Empty; + } + + return data; + } + public void AddGroupNotice(UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket) + { + string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, ""); + + Hashtable param = new Hashtable(); + param["GroupID"] = groupID.ToString(); + param["NoticeID"] = noticeID.ToString(); + param["FromName"] = fromName; + param["Subject"] = subject; + param["Message"] = message; + param["BinaryBucket"] = binBucket; + param["TimeStamp"] = ((uint)Util.UnixTimeSinceEpoch()).ToString(); + + IList parameters = new ArrayList(); + parameters.Add(param); + XmlRpcRequest req = new XmlRpcRequest("groups.addGroupNotice", parameters); + XmlRpcResponse resp = req.Send(m_serviceURL, 10000); + Hashtable respData = (Hashtable)resp.Value; + + List values = new List(); + + if (respData.Contains("error")) + { + LogRespDataToConsoleError(respData); + } + } + + + } + + public class GroupNoticeInfo + { + public GroupNoticeData noticeData = new GroupNoticeData(); + public UUID GroupID = UUID.Zero; + public string Message = string.Empty; + public byte[] BinaryBucket = new byte[0]; + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs new file mode 100644 index 0000000000..d14d13591a --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs @@ -0,0 +1,423 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +//using System.Collections; +using System.Collections.Generic; +using System.Reflection; + + +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Region.CoreModules.Framework.EventQueue; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + + +using Caps = OpenSim.Framework.Communications.Capabilities.Caps; + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + public class XmlRpcGroupsMessaging : INonSharedRegionModule + { + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_SceneList = new List(); + + // must be NonShared for this to work, otherewise we may actually get multiple active clients + private Dictionary m_ActiveClients = new Dictionary(); + + private IMessageTransferModule m_MsgTransferModule = null; + + private IGroupsModule m_GroupsModule = null; + + // Config Options + private bool m_GroupMessagingEnabled = true; + private bool m_debugEnabled = true; + + #region IRegionModule Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + m_log.Info("[GROUPS-MESSAGING]: Initializing XmlRpcGroupsMessaging"); + + if (groupsConfig == null) + { + // Do not run this module by default. + m_log.Info("[GROUPS-MESSAGING]: No config found in OpenSim.ini -- not enabling XmlRpcGroupsMessaging"); + return; + } + else + { + if (!groupsConfig.GetBoolean("Enabled", false)) + { + m_log.Info("[GROUPS-MESSAGING]: Groups disabled in configuration"); + return; + } + + if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") + { + m_log.Info("[GROUPS-MESSAGING]: Config Groups Module not set to XmlRpcGroups"); + + return; + } + + m_GroupMessagingEnabled = groupsConfig.GetBoolean("XmlRpcMessagingEnabled", true); + + if (!m_GroupMessagingEnabled) + { + m_log.Info("[GROUPS-MESSAGING]: XmlRpcGroups Messaging disabled."); + return; + } + + m_debugEnabled = groupsConfig.GetBoolean("XmlRpcDebugEnabled", true); + + } + + m_log.Info("[GROUPS-MESSAGING]: XmlRpcGroupsMessaging starting up"); + + } + + public void AddRegion(Scene scene) + { + } + public void RegionLoaded(Scene scene) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + if (!m_GroupMessagingEnabled) + return; + + + m_GroupsModule = scene.RequestModuleInterface(); + + // No groups module, no groups messaging + if (m_GroupsModule == null) + { + m_GroupMessagingEnabled = false; + m_log.Info("[GROUPS-MESSAGING]: Could not get IGroupsModule, XmlRpcGroupsMessaging is now disabled."); + Close(); + return; + } + + m_MsgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no groups messaging + if (m_MsgTransferModule == null) + { + m_GroupMessagingEnabled = false; + m_log.Info("[GROUPS-MESSAGING]: Could not get MessageTransferModule"); + Close(); + return; + } + + + m_SceneList.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnClientClosed += OnClientClosed; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + + } + + public void RemoveRegion(Scene scene) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_SceneList.Remove(scene); + } + + + public void Close() + { + m_log.Debug("[GROUPS-MESSAGING]: Shutting down XmlRpcGroupsMessaging module."); + + + foreach (Scene scene in m_SceneList) + { + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnClientClosed -= OnClientClosed; + scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; + } + + m_SceneList.Clear(); + + m_GroupsModule = null; + m_MsgTransferModule = null; + } + + public string Name + { + get { return "XmlRpcGroupsMessaging"; } + } + + #endregion + + #region SimGridEventHandlers + + private void OnNewClient(IClientAPI client) + { + RegisterClientAgent(client); + } + private void OnClientClosed(UUID AgentId) + { + UnregisterClientAgent(AgentId); + } + + private void OnGridInstantMessage(GridInstantMessage msg) + { + m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(msg); + + // Incoming message from a group + if ((msg.dialog == (byte)InstantMessageDialog.SessionSend) && (msg.fromGroup == true)) + { + if (m_ActiveClients.ContainsKey(msg.toAgentID)) + { + UUID GroupID = new UUID(msg.fromAgentID); + // SendMessageToGroup(im); + + GroupRecord GroupInfo = m_GroupsModule.GetGroupRecord(GroupID); + if (GroupInfo != null) + { + + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Sending chatterbox invite instant message"); + + // Force? open the group session dialog??? + IEventQueue eq = m_ActiveClients[msg.toAgentID].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 + , Utils.StringToBytes(GroupInfo.GroupName) + ); + + eq.ChatterBoxSessionAgentListUpdates( + new UUID(GroupID) + , new UUID(msg.fromAgentID) + , new UUID(msg.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + + } + } + } + + } + + #endregion + + #region ClientEvents + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + DebugGridInstantMessage(im); + + // Start group IM session + if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) + { + UUID GroupID = new UUID(im.toAgentID); + + GroupRecord GroupInfo = m_GroupsModule.GetGroupRecord(GroupID); + if (GroupInfo != null) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Start Group Session for {0}", GroupInfo.GroupName); + + // remoteClient.SendInstantMessage(new GridInstantMessage(remoteClient.Scene, GroupID, GroupProfile.Name, remoteClient.AgentId, (byte)OpenMetaverse.InstantMessageDialog.SessionSend, true, "Welcome", GroupID, false, new Vector3(), new byte[0])); + + ChatterBoxSessionStartReplyViaCaps(remoteClient, GroupInfo.GroupName, GroupID); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + queue.ChatterBoxSessionAgentListUpdates( + new UUID(GroupID) + , new UUID(im.fromAgentID) + , new UUID(im.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + + // Send a message to a group + if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) + { + UUID GroupID = new UUID(im.toAgentID); + + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Send message to session for group {0}", GroupID); + + SendMessageToGroup(im, GroupID); + } + + // Incoming message from a group + if ((im.dialog == (byte)InstantMessageDialog.SessionSend) && (im.fromGroup == true)) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Message from group session {0} going to agent {1}", im.fromAgentID, im.toAgentID); + } + } + #endregion + + private void RegisterClientAgent(IClientAPI client) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + lock (m_ActiveClients) + { + if (!m_ActiveClients.ContainsKey(client.AgentId.Guid)) + { + client.OnInstantMessage += OnInstantMessage; + + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] OnInstantMessage registered for {0}", client.Name); + + m_ActiveClients.Add(client.AgentId.Guid, client); + } + else + { + // Remove old client connection for this agent + UnregisterClientAgent(client.AgentId); + + // Add new client connection + RegisterClientAgent(client); + } + } + } + private void UnregisterClientAgent(UUID agentID) + { + lock (m_ActiveClients) + { + if (m_ActiveClients.ContainsKey(agentID.Guid)) + { + IClientAPI client = m_ActiveClients[agentID.Guid]; + client.OnInstantMessage -= OnInstantMessage; + + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] OnInstantMessage unregistered for {0}", client.Name); + + m_ActiveClients.Remove(agentID.Guid); + } + else + { + m_log.InfoFormat("[GROUPS-MESSAGING] Client closed that wasn't registered here."); + } + } + } + + private void SendMessageToGroup(GridInstantMessage im, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = im.imSessionID; + msg.fromAgentID = im.imSessionID; // GroupID + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.fromAgentName = im.fromAgentName; + msg.message = im.message; + msg.dialog = im.dialog; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = im.ParentEstateID; + msg.Position = im.Position; + msg.RegionID = im.RegionID; + msg.binaryBucket = new byte[1] { 0 }; + + foreach (GroupMembersData member in m_GroupsModule.GroupMembersRequest(null, groupID)) + { + msg.toAgentID = member.AgentID.Guid; + m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + } + + void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[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(EventQueueHelper.buildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId); + } + + } + + + private void DebugGridInstantMessage(GridInstantMessage im) + { + if (m_debugEnabled) + { + 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")); + } + } + + } +} diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs new file mode 100644 index 0000000000..ca08fe2dde --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs @@ -0,0 +1,1039 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; + +using System.Collections; +//using Nwc.XmlRpc; + +using log4net; +using Nini.Config; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Region.CoreModules.Framework.EventQueue; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +using Caps = OpenSim.Framework.Communications.Capabilities.Caps; +using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; + + + +namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups +{ + public class XmlRpcGroupsModule : INonSharedRegionModule, IGroupsModule + { + /// + /// To use this module, you must specify the following in your OpenSim.ini + /// [GROUPS] + /// Enabled = true + /// Module = XmlRpcGroups + /// XmlRpcMessagingEnabled = true + /// XmlRpcNoticesEnabled = true + /// XmlRpcDebugEnabled = true + /// + /// + + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_SceneList = new List(); + + // This only works when running as non-Shared, in shared, there may be multiple IClientAPIs for a single client + private Dictionary m_ActiveClients = new Dictionary(); + + private IMessageTransferModule m_MsgTransferModule = null; + + private IGroupDataProvider m_groupData = null; + + // Configuration settings + private const string m_defaultXmlRpcServiceURL = "http://osflotsam.org/xmlrpc.php"; + private bool m_GroupsEnabled = false; + private bool m_GroupNoticesEnabled = true; + private bool m_debugEnabled = true; + + #region IRegionModule Members + + public void Initialise(IConfigSource config) + { + IConfig groupsConfig = config.Configs["Groups"]; + + m_log.Info("[GROUPS]: Initializing XmlRpcGroups"); + + if (groupsConfig == null) + { + // Do not run this module by default. + m_log.Info("[GROUPS]: No config found in OpenSim.ini -- not enabling XmlRpcGroups"); + return; + } + else + { + m_GroupsEnabled = groupsConfig.GetBoolean("Enabled", false); + if (!m_GroupsEnabled) + { + m_log.Info("[GROUPS]: Groups disabled in configuration"); + return; + } + + if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") + { + m_log.Info("[GROUPS]: Config Groups Module not set to XmlRpcGroups"); + + return; + } + + string ServiceURL = groupsConfig.GetString("XmlRpcServiceURL", m_defaultXmlRpcServiceURL); + m_groupData = new XmlRpcGroupDataProvider(ServiceURL); + m_log.InfoFormat("[GROUPS]: XmlRpc Service URL set to: {0}", ServiceURL); + + m_GroupNoticesEnabled = groupsConfig.GetBoolean("XmlRpcNoticesEnabled", true); + m_debugEnabled = groupsConfig.GetBoolean("XmlRpcDebugEnabled", true); + + } + } + + public void AddRegion(Scene scene) + { + scene.RegisterModuleInterface(this); + } + public void RegionLoaded(Scene scene) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + if (!m_GroupsEnabled) + return; + + + m_MsgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no notices, group invites, rejects, ejects, etc + if (m_MsgTransferModule == null) + { + m_GroupsEnabled = false; + m_log.Info("[GROUPS]: Could not get MessageTransferModule"); + Close(); + return; + } + + + m_SceneList.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnClientClosed += OnClientClosed; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + + } + + public void RemoveRegion(Scene scene) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_SceneList.Remove(scene); + } + + public void Close() + { + m_log.Debug("[GROUPS]: Shutting down XmlRpcGroups module."); + } + + public string Name + { + get { return "XmlRpcGroupsModule"; } + } + + #endregion + + private void UpdateAllClientsWithGroupInfo() + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + foreach (IClientAPI client in m_ActiveClients.Values) + { + UpdateClientWithGroupInfo(client); + } + } + + private void UpdateClientWithGroupInfo(IClientAPI client) + { + m_log.InfoFormat("[GROUPS] {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, client.Name); + OnAgentDataUpdateRequest(client, client.AgentId, UUID.Zero); + + + // Need to send a group membership update to the client + // UDP version doesn't seem to behave nicely + // client.SendGroupMembership(GetMembershipData(client.AgentId)); + + GroupMembershipData[] membershipData = m_groupData.GetAgentGroupMemberships(client.AgentId).ToArray(); + + SendGroupMembershipInfoViaCaps(client, membershipData); + client.SendAvatarGroupsReply(client.AgentId, membershipData); + + } + + #region EventHandlers + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + + lock (m_ActiveClients) + { + if (!m_ActiveClients.ContainsKey(client.AgentId)) + { + client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; + client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; + client.OnDirFindQuery += OnDirFindQuery; + client.OnInstantMessage += OnInstantMessage; + + m_ActiveClients.Add(client.AgentId, client); + } + } + + UpdateClientWithGroupInfo(client); + } + private void OnClientClosed(UUID AgentId) + { + if (m_debugEnabled) m_log.InfoFormat("[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 + { + m_log.InfoFormat("[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) + { + m_log.InfoFormat("[GROUPS] {0} called with queryText({1}) queryFlags({2}) queryStart({3})", System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart); + + remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(queryText).ToArray()); + } + + } + + private void OnAgentDataUpdateRequest(IClientAPI remoteClient, + UUID AgentID, UUID SessionID) + { + m_log.InfoFormat("[GROUPS] {0} called with SessionID :: {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, SessionID); + + + UUID ActiveGroupID = UUID.Zero; + string ActiveGroupTitle = string.Empty; + string ActiveGroupName = string.Empty; + ulong ActiveGroupPowers = (ulong)GroupPowers.None; + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(AgentID); + if (membership != null) + { + ActiveGroupID = membership.GroupID; + ActiveGroupTitle = membership.GroupTitle; + ActiveGroupPowers = membership.GroupPowers; + } + + string firstname, lastname; + IClientAPI agent; + if( m_ActiveClients.TryGetValue(AgentID, out agent) ) + { + firstname = agent.FirstName; + lastname = agent.LastName; + } else { + firstname = "Unknown"; + lastname = "Unknown"; + } + + UpdateScenePresenceWithTitle(AgentID, ActiveGroupTitle); + + remoteClient.SendAgentDataUpdate(AgentID, ActiveGroupID, firstname, + lastname, ActiveGroupPowers, ActiveGroupName, + ActiveGroupTitle); + } + + private void HandleUUIDGroupNameRequest(UUID GroupID,IClientAPI remote_client) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string GroupName; + + GroupRecord group = m_groupData.GetGroupRecord(GroupID, null); + if (group != null) + { + GroupName = group.GroupName; + } + else + { + GroupName = "Unknown"; + } + + + remote_client.SendGroupNameReply(GroupID, GroupName); + } + + + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + + // Group invitations + if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) + { + m_log.WarnFormat("[GROUPS] Received an IIM for {0}.", ((InstantMessageDialog)im.dialog).ToString()); + + + UUID inviteID = new UUID(im.imSessionID); + GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(inviteID); + + m_log.WarnFormat("[GROUPS] Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); + + UUID fromAgentID = new UUID(im.fromAgentID); + if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID)) + { + + // Accept + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) + { + m_log.WarnFormat("[GROUPS] Received an accept invite notice."); + + // and the sessionid is the role + m_groupData.AddAgentToGroup(inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID); + + if (m_MsgTransferModule != null) + { + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = UUID.Zero.Guid; + msg.toAgentID = inviteInfo.AgentID.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]; + + m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + + UpdateAllClientsWithGroupInfo(); + + m_groupData.RemoveAgentToGroupInvite(inviteID); + } + + // Reject + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) + { + m_log.WarnFormat("[GROUPS] Received a reject invite notice."); + m_groupData.RemoveAgentToGroupInvite(inviteID); + + } + + + } + } + + // Group notices + if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) + { + if (!m_GroupNoticesEnabled) + { + return; + } + + UUID GroupID = new UUID(im.toAgentID); + if( m_groupData.GetGroupRecord(GroupID, null) != null) + { + UUID NoticeID = UUID.Random(); + string Subject = im.message.Substring(0, im.message.IndexOf('|')); + string Message = im.message.Substring(Subject.Length + 1); + + byte[] bucket; + + if ((im.binaryBucket.Length == 1) && (im.binaryBucket[0] == 0)) + { + bucket = new byte[19]; + bucket[0] = 0; //dunno + bucket[1] = 0; //dunno + GroupID.ToBytes(bucket, 2); + bucket[18] = 0; //dunno + } + else + { + string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); + binBucket = binBucket.Remove(0, 14).Trim(); + m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket); + + OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); + + foreach (string key in binBucketOSD.Keys) + { + m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString()); + } + + // treat as if no attachment + bucket = new byte[19]; + bucket[0] = 0; //dunno + bucket[1] = 0; //dunno + GroupID.ToBytes(bucket, 2); + bucket[18] = 0; //dunno + } + + + m_groupData.AddGroupNotice(GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); + if (OnNewGroupNotice != null) + { + OnNewGroupNotice(GroupID, NoticeID); + } + + // Build notice IIM + GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); + + // Send notice out to everyone that wants notices + foreach( GroupMembersData member in m_groupData.GetGroupMembers(GroupID) ) + { + if( member.AcceptNotices ) + { + msg.toAgentID = member.AgentID.Guid; + m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + + } + } + + + + } + } + + // 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. + + + if (m_MsgTransferModule != null) + { + im.dialog = (byte)InstantMessageDialog.MessageFromAgent; + m_MsgTransferModule.SendInstantMessage(im, delegate(bool success) { }); + } + + UUID ejecteeID = new UUID(im.toAgentID); + UUID groupID = new UUID(im.toAgentID); + if (m_ActiveClients.ContainsKey(ejecteeID)) + { + m_ActiveClients[ejecteeID].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); + } + + + #endregion + + + private void UpdateScenePresenceWithTitle(UUID AgentID, string Title) + { + m_log.DebugFormat("[GROUPS] Updating scene title for {0} with title: {1}", AgentID, Title); + ScenePresence presence = null; + lock (m_SceneList) + { + foreach (Scene scene in m_SceneList) + { + presence = scene.GetScenePresence(AgentID); + if (presence != null) + { + presence.Grouptitle = Title; + + // FixMe: Ter suggests a "Schedule" method that I can't find. + presence.SendFullUpdateToAllClients(); + } + } + } + } + + + #region IGroupsModule Members + + public event NewGroupNotice OnNewGroupNotice; + + public GroupRecord GetGroupRecord(UUID GroupID) + { + return m_groupData.GetGroupRecord(GroupID, null); + } + + public void ActivateGroup(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroup(remoteClient.AgentId, groupID); + + // UpdateClientWithGroupInfo(remoteClient); + UpdateAllClientsWithGroupInfo(); + } + + /// + /// Get the Role Titles for an Agent, for a specific group + /// + public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List agentRoles = m_groupData.GetAgentGroupRoles(remoteClient.AgentId, groupID); + GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(remoteClient.AgentId, groupID); + + List titles = new List(); + foreach (GroupRolesData role in agentRoles) + { + GroupTitlesData title = new GroupTitlesData(); + title.Name = role.Name; + title.Selected = agentMembership.ActiveRole == role.RoleID; + title.UUID = role.RoleID; + } + + return titles; + } + + public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupMembers(groupID); + + foreach (GroupMembersData member in data) + { + m_log.InfoFormat("[GROUPS] {0} {1}", member.AgentID, member.Title); + } + + return data; + + } + + public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoles(groupID); + + foreach (GroupRolesData member in data) + { + m_log.InfoFormat("[GROUPS] {0} {1}", member.Title, member.Members); + } + + return data; + + } + + public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoleMembers(groupID); + + foreach (GroupRoleMembersData member in data) + { + m_log.InfoFormat("[GROUPS] Av: {0} Role: {1}", member.MemberID, member.RoleID); + } + + return data; + + + } + + public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupProfileData profile = new GroupProfileData(); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(groupID, null); + if (groupInfo != null) + { + profile.AllowPublish = groupInfo.AllowPublish; + profile.Charter = groupInfo.Charter; + profile.FounderID = groupInfo.FounderID; + profile.GroupID = groupID; + profile.GroupMembershipCount = m_groupData.GetGroupMembers(groupID).Count; + profile.GroupRolesCount = m_groupData.GetGroupRoles(groupID).Count; + profile.InsigniaID = groupInfo.GroupPicture; + profile.MaturePublish = groupInfo.MaturePublish; + profile.MembershipFee = groupInfo.MembershipFee; + profile.Money = 0; // TODO: Get this from the currency server? + profile.Name = groupInfo.GroupName; + profile.OpenEnrollment = groupInfo.OpenEnrollment; + profile.OwnerRole = groupInfo.OwnerRoleID; + profile.ShowInList = groupInfo.ShowInList; + } + + GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(remoteClient.AgentId, groupID); + if (memberInfo != null) + { + profile.MemberTitle = memberInfo.GroupTitle; + profile.PowersMask = memberInfo.GroupPowers; + } + + return profile; + } + + public GroupMembershipData[] GetMembershipData(UUID UserID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + return m_groupData.GetAgentGroupMemberships(UserID).ToArray(); + } + + public GroupMembershipData GetMembershipData(UUID GroupID, UUID UserID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + return m_groupData.GetAgentGroupMembership(UserID, 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.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Security Check? + + m_groupData.UpdateGroup(groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish); + } + + public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile) + { + // TODO: Security Check? + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentGroupInfo(remoteClient.AgentId, 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.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + if( m_groupData.GetGroupRecord(UUID.Zero, name) != null ) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists."); + return UUID.Zero; + } + + UUID GroupID = m_groupData.CreateGroup(name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, remoteClient.AgentId); + + remoteClient.SendCreateGroupReply(GroupID, true, "Group created successfullly"); + + UpdateClientWithGroupInfo(remoteClient); + + return GroupID; + } + + public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID GroupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // ToDo: check if agent is a member of group and is allowed to see notices? + + return m_groupData.GetGroupNotices(GroupID).ToArray(); + } + + /// + /// Get the title of the agent's current role. + /// + public string GetGroupTitle(UUID avatarID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(avatarID); + 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.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroupRole(remoteClient.AgentId, GroupID, TitleRoleID); + + UpdateAllClientsWithGroupInfo(); + } + + + public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Security Checks? + + switch ((OpenMetaverse.GroupRoleUpdate)updateType) + { + case OpenMetaverse.GroupRoleUpdate.Create: + m_groupData.AddGroupRole(groupID, UUID.Random(), name, description, title, powers); + break; + + case OpenMetaverse.GroupRoleUpdate.Delete: + m_groupData.RemoveGroupRole(groupID, roleID); + break; + + case OpenMetaverse.GroupRoleUpdate.UpdateAll: + case OpenMetaverse.GroupRoleUpdate.UpdateData: + case OpenMetaverse.GroupRoleUpdate.UpdatePowers: + m_groupData.UpdateGroupRole(groupID, roleID, name, description, title, powers); + break; + + case OpenMetaverse.GroupRoleUpdate.NoUpdate: + default: + // No Op + break; + + } + + UpdateClientWithGroupInfo(remoteClient); + } + + public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + // Todo: Security check + + switch (changes) + { + case 0: + // Add + m_groupData.AddAgentToGroupRole(memberID, groupID, roleID); + + break; + case 1: + // Remove + m_groupData.RemoveAgentFromGroupRole(memberID, groupID, roleID); + + break; + default: + m_log.ErrorFormat("[GROUPS] {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); + break; + } + UpdateClientWithGroupInfo(remoteClient); + } + + public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + + GroupNoticeInfo data = m_groupData.GetGroupNotice(groupNoticeID); + + if (data != null) + { + if (m_MsgTransferModule != null) + { + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = data.GroupID.Guid; + msg.toAgentID = remoteClient.AgentId.Guid; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.fromAgentName = "Group Notice From"; + 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; + + m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + } + + } + + public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) + { + m_log.WarnFormat("[GROUPS] {0} is probably not properly implemented", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.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(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; + msg.binaryBucket = info.BinaryBucket; + } + + return msg; + } + + public void SendAgentGroupDataUpdate(IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + UpdateClientWithGroupInfo(remoteClient); + } + + public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Should check to see if OpenEnrollment, or if there's an outstanding invitation + m_groupData.AddAgentToGroup(remoteClient.AgentId, groupID, UUID.Zero); + + remoteClient.SendJoinGroupReply(groupID, true); + + UpdateClientWithGroupInfo(remoteClient); + } + + public void LeaveGroupRequest(IClientAPI remoteClient, UUID GroupID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.RemoveAgentFromGroup(remoteClient.AgentId, GroupID); + + remoteClient.SendLeaveGroupReply(GroupID, true); + + remoteClient.SendAgentDropGroup(GroupID); + + UpdateClientWithGroupInfo(remoteClient); + } + + public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID GroupID, UUID EjecteeID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Todo: Security check? + m_groupData.RemoveAgentFromGroup(EjecteeID, GroupID); + + remoteClient.SendEjectGroupMemberReply(remoteClient.AgentId, GroupID, true); + + if (m_MsgTransferModule != null) + { + GroupRecord groupInfo = m_groupData.GetGroupRecord(GroupID, null); + UserProfileData userProfile = m_SceneList[0].CommsManager.UserService.GetUserProfile(EjecteeID); + + if ((groupInfo == null) || (userProfile == null)) + { + return; + } + + + // Send Message to Ejectee + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = remoteClient.AgentId.Guid; + // msg.fromAgentID = info.GroupID; + msg.toAgentID = EjecteeID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = remoteClient.Name; + msg.message = string.Format("You have been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent; + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) { m_log.DebugFormat("[GROUPS] Message Sent Success: {0}", success,ToString()); }); + + + // 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 = remoteClient.AgentId.Guid; + msg.toAgentID = remoteClient.AgentId.Guid; + msg.timestamp = 0; + msg.fromAgentName = remoteClient.Name; + if (userProfile != null) + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName, userProfile.Name); + } + else + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, 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 = remoteClient.Scene.RegionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) { m_log.DebugFormat("[GROUPS] Message Sent Success: {0}", success, ToString()); }); + + + + } + + + UpdateAllClientsWithGroupInfo(); + } + + public void InviteGroupRequest(IClientAPI remoteClient, UUID GroupID, UUID InvitedAgentID, UUID RoleID) + { + if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + m_log.WarnFormat("[GROUPS] GID {0}, AID {1}, RID {2} ", GroupID, InvitedAgentID, RoleID); + + // Todo: Security check, probably also want to send some kind of notification + UUID InviteID = UUID.Random(); + m_log.WarnFormat("[GROUPS] Invite ID: {0}", InviteID); + m_groupData.AddAgentToGroupInvite(InviteID, GroupID, RoleID, InvitedAgentID); + + if (m_MsgTransferModule != null) + { + Guid inviteUUID = InviteID.Guid; + + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = inviteUUID; + + // msg.fromAgentID = remoteClient.AgentId.Guid; + msg.fromAgentID = GroupID.Guid; + msg.toAgentID = InvitedAgentID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = remoteClient.Name; + msg.message = string.Format("{0} has invited you to join a group. There is no cost to join this group.", remoteClient.Name); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid; + msg.binaryBucket = new byte[20]; + + m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) { m_log.DebugFormat("[GROUPS] Message Sent Success: {0}", success,ToString()); }); + } + } + + #endregion + + void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, 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(remoteClient.AgentId)); + AgentData.Add(AgentDataMap); + + + OSDArray GroupData = new OSDArray(data.Length); + OSDArray NewGroupData = new OSDArray(data.Length); + + foreach (GroupMembershipData membership in data) + { + OSDMap GroupDataMap = new OSDMap(6); + OSDMap NewGroupDataMap = new OSDMap(1); + + GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID)); + GroupDataMap.Add("GroupPowers", OSD.FromBinary(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); + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + + if (queue != null) + { + queue.Enqueue(EventQueueHelper.buildEvent("AgentGroupDataUpdate", llDataStruct), remoteClient.AgentId); + } + + } + } + +} diff --git a/OpenSim/Region/OptionalModules/Resources/OptionalModules.addin.xml b/OpenSim/Region/OptionalModules/Resources/OptionalModules.addin.xml new file mode 100644 index 0000000000..4795edc26c --- /dev/null +++ b/OpenSim/Region/OptionalModules/Resources/OptionalModules.addin.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 127501cc42..27aa5e0cb6 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1229,4 +1229,26 @@ freeswitch_echo_port = 50505 freeswitch_well_known_ip = ip.address.of.freeswitch.server freeswitch_default_timeout = 5000 freeswitch_subscribe_retry = 120 -; freeswitch_password_reset_url = \ No newline at end of file +; freeswitch_password_reset_url = + + +[Groups] + Enabled = false + + ; This is the current groups stub in Region.CoreModules.Avatar.Groups + Module = Default + + + ; The XmlRpcGroups implementation can be used against the publically available service + ; that I have made available for testing. Your group data is not guarenteed safe + ; or available if you use this service, but it's available now and seems to work. + ; The PHP code for the service is available for you to deploy to your own server. + ; + ;Module = XmlRpcGroups + + ;XmlRpcServiceURL = http://osflotsam.org/xmlrpc.php + ;XmlRpcMessagingEnabled = true + ;XmlRpcNoticesEnabled = true + + ; This makes the XmlRpcGroups modules very chatty on the console. + ;XmlRpcDebugEnabled = true diff --git a/prebuild.xml b/prebuild.xml index 72cfb7604d..7d33fac8e5 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -1268,6 +1268,7 @@ + @@ -1277,10 +1278,12 @@ + + @@ -3289,3 +3292,4 @@ +