/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Reflection; using System.Threading; using Nwc.XmlRpc; using log4net; using Mono.Addins; using Nini.Config; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Services.Interfaces; /*************************************************************************** * Simian Data Map * =============== * * OwnerID -> Type -> Key * ----------------------- * * UserID -> Group -> ActiveGroup * + GroupID * * UserID -> GroupSessionDropped -> GroupID * UserID -> GroupSessionInvited -> GroupID * * UserID -> GroupMember -> GroupID * + SelectedRoleID [UUID] * + AcceptNotices [bool] * + ListInProfile [bool] * + Contribution [int] * * UserID -> GroupRole[GroupID] -> RoleID * * * GroupID -> Group -> GroupName * + Charter * + ShowInList * + InsigniaID * + MembershipFee * + OpenEnrollment * + AllowPublish * + MaturePublish * + FounderID * + EveryonePowers * + OwnerRoleID * + OwnersPowers * * GroupID -> GroupRole -> RoleID * + Name * + Description * + Title * + Powers * * GroupID -> GroupMemberInvite -> InviteID * + AgentID * + RoleID * * GroupID -> GroupNotice -> NoticeID * + TimeStamp [uint] * + FromName [string] * + Subject [string] * + Message [string] * + BinaryBucket [byte[]] * * */ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianGroupsServicesConnectorModule")] public class SimianGroupsServicesConnectorModule : ISharedRegionModule, IGroupsServicesConnector { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public const GroupPowers m_DefaultEveryonePowers = GroupPowers.AllowSetHome | GroupPowers.Accountable | GroupPowers.JoinChat | GroupPowers.AllowVoiceChat | GroupPowers.ReceiveNotices | GroupPowers.StartProposal | GroupPowers.VoteOnProposal; // Would this be cleaner as (GroupPowers)ulong.MaxValue; public const GroupPowers m_DefaultOwnerPowers = 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; private bool m_connectorEnabled = false; private string m_groupsServerURI = string.Empty; private bool m_debugEnabled = false; private Dictionary m_pendingRequests = new Dictionary(); private ExpiringCache m_memoryCache; private int m_cacheTimeout = 30; // private IUserAccountService m_accountService = null; #region Region Module interfaceBase Members public string Name { get { return "SimianGroupsServicesConnector"; } } // this module is not intended to be replaced, but there should only be 1 of them. public Type ReplaceableInterface { get { return null; } } public void Initialise(IConfigSource config) { IConfig groupsConfig = config.Configs["Groups"]; if (groupsConfig == null) { // Do not run this module by default. return; } else { // 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("ServicesConnectorModule", "XmlRpcGroupsServicesConnector") != Name)) { m_connectorEnabled = false; return; } m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name); m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); if (string.IsNullOrEmpty(m_groupsServerURI)) { m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]"); m_connectorEnabled = false; return; } m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30); if (m_cacheTimeout == 0) { m_log.WarnFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Disabled."); } else { m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Timeout set to {0}.", m_cacheTimeout); } m_memoryCache = new ExpiringCache(); // If we got all the config options we need, lets start'er'up m_connectorEnabled = true; m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true); } } public void Close() { } public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) { if (m_connectorEnabled) { scene.RegisterModuleInterface(this); } } public void RemoveRegion(OpenSim.Region.Framework.Scenes.Scene scene) { if (scene.RequestModuleInterface() == this) { scene.UnregisterModuleInterface(this); } } public void RegionLoaded(OpenSim.Region.Framework.Scenes.Scene scene) { // TODO: May want to consider listenning for Agent Connections so we can pre-cache group info // scene.EventManager.OnNewClient += OnNewClient; } #endregion #region ISharedRegionModule Members public void PostInitialise() { // NoOp } #endregion #region IGroupsServicesConnector Members /// /// 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(UUID requestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); UUID GroupID = UUID.Random(); UUID OwnerRoleID = UUID.Random(); OSDMap GroupInfoMap = new OSDMap(); GroupInfoMap["Charter"] = OSD.FromString(charter); GroupInfoMap["ShowInList"] = OSD.FromBoolean(showInList); GroupInfoMap["InsigniaID"] = OSD.FromUUID(insigniaID); GroupInfoMap["MembershipFee"] = OSD.FromInteger(0); GroupInfoMap["OpenEnrollment"] = OSD.FromBoolean(openEnrollment); GroupInfoMap["AllowPublish"] = OSD.FromBoolean(allowPublish); GroupInfoMap["MaturePublish"] = OSD.FromBoolean(maturePublish); GroupInfoMap["FounderID"] = OSD.FromUUID(founderID); GroupInfoMap["EveryonePowers"] = OSD.FromULong((ulong)m_DefaultEveryonePowers); GroupInfoMap["OwnerRoleID"] = OSD.FromUUID(OwnerRoleID); GroupInfoMap["OwnersPowers"] = OSD.FromULong((ulong)m_DefaultOwnerPowers); if (SimianAddGeneric(GroupID, "Group", name, GroupInfoMap)) { AddGroupRole(requestingAgentID, GroupID, UUID.Zero, "Everyone", "Members of " + name, "Member of " + name, (ulong)m_DefaultEveryonePowers); AddGroupRole(requestingAgentID, GroupID, OwnerRoleID, "Owners", "Owners of " + name, "Owner of " + name, (ulong)m_DefaultOwnerPowers); AddAgentToGroup(requestingAgentID, requestingAgentID, GroupID, OwnerRoleID); return GroupID; } else { return UUID.Zero; } } public void UpdateGroup(UUID requestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // TODO: Check to make sure requestingAgentID has permission to update group string GroupName; OSDMap GroupInfoMap; if (SimianGetFirstGenericEntry(groupID, "GroupInfo", out GroupName, out GroupInfoMap)) { GroupInfoMap["Charter"] = OSD.FromString(charter); GroupInfoMap["ShowInList"] = OSD.FromBoolean(showInList); GroupInfoMap["InsigniaID"] = OSD.FromUUID(insigniaID); GroupInfoMap["MembershipFee"] = OSD.FromInteger(0); GroupInfoMap["OpenEnrollment"] = OSD.FromBoolean(openEnrollment); GroupInfoMap["AllowPublish"] = OSD.FromBoolean(allowPublish); GroupInfoMap["MaturePublish"] = OSD.FromBoolean(maturePublish); SimianAddGeneric(groupID, "Group", GroupName, GroupInfoMap); } } public void AddGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap GroupRoleInfo = new OSDMap(); GroupRoleInfo["Name"] = OSD.FromString(name); GroupRoleInfo["Description"] = OSD.FromString(description); GroupRoleInfo["Title"] = OSD.FromString(title); GroupRoleInfo["Powers"] = OSD.FromULong((ulong)powers); // TODO: Add security, make sure that requestingAgentID has permision to add roles SimianAddGeneric(groupID, "GroupRole", roleID.ToString(), GroupRoleInfo); } public void RemoveGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // TODO: Add security // Can't delete the Everyone Role if (roleID != UUID.Zero) { // Remove all GroupRole Members from Role Dictionary GroupRoleMembers; string GroupRoleMemberType = "GroupRole" + groupID.ToString(); if (SimianGetGenericEntries(GroupRoleMemberType, roleID.ToString(), out GroupRoleMembers)) { foreach (UUID UserID in GroupRoleMembers.Keys) { EnsureRoleNotSelectedByMember(groupID, roleID, UserID); SimianRemoveGenericEntry(UserID, GroupRoleMemberType, roleID.ToString()); } } // Remove role SimianRemoveGenericEntry(groupID, "GroupRole", roleID.ToString()); } } public void UpdateGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // TODO: Security, check that requestingAgentID is allowed to update group roles OSDMap GroupRoleInfo; if (SimianGetGenericEntry(groupID, "GroupRole", roleID.ToString(), out GroupRoleInfo)) { if (name != null) { GroupRoleInfo["Name"] = OSD.FromString(name); } if (description != null) { GroupRoleInfo["Description"] = OSD.FromString(description); } if (title != null) { GroupRoleInfo["Title"] = OSD.FromString(title); } GroupRoleInfo["Powers"] = OSD.FromULong((ulong)powers); } SimianAddGeneric(groupID, "GroupRole", roleID.ToString(), GroupRoleInfo); } public GroupRecord GetGroupRecord(UUID requestingAgentID, UUID groupID, string groupName) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap GroupInfoMap = null; if (groupID != UUID.Zero) { if (!SimianGetFirstGenericEntry(groupID, "Group", out groupName, out GroupInfoMap)) { return null; } } else if (!string.IsNullOrEmpty(groupName)) { if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap)) { return null; } } GroupRecord GroupInfo = new GroupRecord(); GroupInfo.GroupID = groupID; GroupInfo.GroupName = groupName; GroupInfo.Charter = GroupInfoMap["Charter"].AsString(); GroupInfo.ShowInList = GroupInfoMap["ShowInList"].AsBoolean(); GroupInfo.GroupPicture = GroupInfoMap["InsigniaID"].AsUUID(); GroupInfo.MembershipFee = GroupInfoMap["MembershipFee"].AsInteger(); GroupInfo.OpenEnrollment = GroupInfoMap["OpenEnrollment"].AsBoolean(); GroupInfo.AllowPublish = GroupInfoMap["AllowPublish"].AsBoolean(); GroupInfo.MaturePublish = GroupInfoMap["MaturePublish"].AsBoolean(); GroupInfo.FounderID = GroupInfoMap["FounderID"].AsUUID(); GroupInfo.OwnerRoleID = GroupInfoMap["OwnerRoleID"].AsUUID(); return GroupInfo; } public GroupProfileData GetMemberGroupProfile(UUID requestingAgentID, UUID groupID, UUID memberID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap groupProfile; string groupName; if (!SimianGetFirstGenericEntry(groupID, "Group", out groupName, out groupProfile)) { // GroupProfileData is not nullable return new GroupProfileData(); } GroupProfileData MemberGroupProfile = new GroupProfileData(); MemberGroupProfile.GroupID = groupID; MemberGroupProfile.Name = groupName; if (groupProfile["Charter"] != null) { MemberGroupProfile.Charter = groupProfile["Charter"].AsString(); } MemberGroupProfile.ShowInList = groupProfile["ShowInList"].AsString() == "1"; MemberGroupProfile.InsigniaID = groupProfile["InsigniaID"].AsUUID(); MemberGroupProfile.MembershipFee = groupProfile["MembershipFee"].AsInteger(); MemberGroupProfile.OpenEnrollment = groupProfile["OpenEnrollment"].AsBoolean(); MemberGroupProfile.AllowPublish = groupProfile["AllowPublish"].AsBoolean(); MemberGroupProfile.MaturePublish = groupProfile["MaturePublish"].AsBoolean(); MemberGroupProfile.FounderID = groupProfile["FounderID"].AsUUID();; MemberGroupProfile.OwnerRole = groupProfile["OwnerRoleID"].AsUUID(); Dictionary Members; if (SimianGetGenericEntries("GroupMember",groupID.ToString(), out Members)) { MemberGroupProfile.GroupMembershipCount = Members.Count; } Dictionary Roles; if (SimianGetGenericEntries(groupID, "GroupRole", out Roles)) { MemberGroupProfile.GroupRolesCount = Roles.Count; } // TODO: Get Group Money balance from somewhere // group.Money = 0; GroupMembershipData MemberInfo = GetAgentGroupMembership(requestingAgentID, memberID, groupID); MemberGroupProfile.MemberTitle = MemberInfo.GroupTitle; MemberGroupProfile.PowersMask = MemberInfo.GroupPowers; return MemberGroupProfile; } public void SetAgentActiveGroup(UUID requestingAgentID, UUID agentID, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap ActiveGroup = new OSDMap(); ActiveGroup.Add("GroupID", OSD.FromUUID(groupID)); SimianAddGeneric(agentID, "Group", "ActiveGroup", ActiveGroup); } public void SetAgentActiveGroupRole(UUID requestingAgentID, UUID agentID, UUID groupID, UUID roleID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap GroupMemberInfo; if (!SimianGetGenericEntry(agentID, "GroupMember", groupID.ToString(), out GroupMemberInfo)) { GroupMemberInfo = new OSDMap(); } GroupMemberInfo["SelectedRoleID"] = OSD.FromUUID(roleID); SimianAddGeneric(agentID, "GroupMember", groupID.ToString(), GroupMemberInfo); } public void SetAgentGroupInfo(UUID requestingAgentID, UUID agentID, UUID groupID, bool acceptNotices, bool listInProfile) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap GroupMemberInfo; if (!SimianGetGenericEntry(agentID, "GroupMember", groupID.ToString(), out GroupMemberInfo)) { GroupMemberInfo = new OSDMap(); } GroupMemberInfo["AcceptNotices"] = OSD.FromBoolean(acceptNotices); GroupMemberInfo["ListInProfile"] = OSD.FromBoolean(listInProfile); GroupMemberInfo["Contribution"] = OSD.FromInteger(0); GroupMemberInfo["SelectedRole"] = OSD.FromUUID(UUID.Zero); SimianAddGeneric(agentID, "GroupMember", groupID.ToString(), GroupMemberInfo); } public void AddAgentToGroupInvite(UUID requestingAgentID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap Invite = new OSDMap(); Invite["AgentID"] = OSD.FromUUID(agentID); Invite["RoleID"] = OSD.FromUUID(roleID); SimianAddGeneric(groupID, "GroupMemberInvite", inviteID.ToString(), Invite); } public GroupInviteInfo GetAgentToGroupInvite(UUID requestingAgentID, UUID inviteID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap GroupMemberInvite; UUID GroupID; if (!SimianGetFirstGenericEntry("GroupMemberInvite", inviteID.ToString(), out GroupID, out GroupMemberInvite)) { return null; } GroupInviteInfo inviteInfo = new GroupInviteInfo(); inviteInfo.InviteID = inviteID; inviteInfo.GroupID = GroupID; inviteInfo.AgentID = GroupMemberInvite["AgentID"].AsUUID(); inviteInfo.RoleID = GroupMemberInvite["RoleID"].AsUUID(); return inviteInfo; } public void RemoveAgentToGroupInvite(UUID requestingAgentID, UUID inviteID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); GroupInviteInfo invite = GetAgentToGroupInvite(requestingAgentID, inviteID); SimianRemoveGenericEntry(invite.GroupID, "GroupMemberInvite", inviteID.ToString()); } public void AddAgentToGroup(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // Setup Agent/Group information SetAgentGroupInfo(requestingAgentID, AgentID, GroupID, true, true); // Add agent to Everyone Group AddAgentToGroupRole(requestingAgentID, AgentID, GroupID, UUID.Zero); // Add agent to Specified Role AddAgentToGroupRole(requestingAgentID, AgentID, GroupID, RoleID); // Set selected role in this group to specified role SetAgentActiveGroupRole(requestingAgentID, AgentID, GroupID, RoleID); } public void RemoveAgentFromGroup(UUID requestingAgentID, UUID agentID, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // If current active group is the group the agent is being removed from, change their group to UUID.Zero GroupMembershipData memberActiveMembership = GetAgentActiveMembership(requestingAgentID, agentID); if (memberActiveMembership.GroupID == groupID) { SetAgentActiveGroup(agentID, agentID, UUID.Zero); } // Remove Group Member information for this group SimianRemoveGenericEntry(agentID, "GroupMember", groupID.ToString()); // By using a Simian Generics Type consisting of a prefix and a groupID, // combined with RoleID as key allows us to get a list of roles a particular member // of a group is assigned to. string GroupRoleMemberType = "GroupRole" + groupID.ToString(); // Take Agent out of all other group roles Dictionary GroupRoles; if (SimianGetGenericEntries(agentID, GroupRoleMemberType, out GroupRoles)) { foreach (string roleID in GroupRoles.Keys) { SimianRemoveGenericEntry(agentID, GroupRoleMemberType, roleID); } } } public void AddAgentToGroupRole(UUID requestingAgentID, UUID agentID, UUID groupID, UUID roleID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); SimianAddGeneric(agentID, "GroupRole" + groupID.ToString(), roleID.ToString(), new OSDMap()); } public void RemoveAgentFromGroupRole(UUID requestingAgentID, UUID agentID, UUID groupID, UUID roleID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // Cannot remove members from the Everyone Role if (roleID != UUID.Zero) { EnsureRoleNotSelectedByMember(groupID, roleID, agentID); string GroupRoleMemberType = "GroupRole" + groupID.ToString(); SimianRemoveGenericEntry(agentID, GroupRoleMemberType, roleID.ToString()); } } public List FindGroups(UUID requestingAgentID, string search) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List findings = new List(); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetGenerics" }, { "Type", "Group" }, { "Key", search }, { "Fuzzy", "1" } }; OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)response["Entries"]; foreach (OSDMap entryMap in entryArray) { DirGroupsReplyData data = new DirGroupsReplyData(); data.groupID = entryMap["OwnerID"].AsUUID(); data.groupName = entryMap["Key"].AsString(); // TODO: is there a better way to do this? Dictionary Members; if (SimianGetGenericEntries("GroupMember", data.groupID.ToString(), out Members)) { data.members = Members.Count; } else { data.members = 0; } // TODO: sort results? // data.searchOrder = order; findings.Add(data); } } return findings; } public GroupMembershipData GetAgentGroupMembership(UUID requestingAgentID, UUID agentID, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); GroupMembershipData data = null; // bool foundData = false; OSDMap UserGroupMemberInfo; if (SimianGetGenericEntry(agentID, "GroupMember", groupID.ToString(), out UserGroupMemberInfo)) { data = new GroupMembershipData(); data.AcceptNotices = UserGroupMemberInfo["AcceptNotices"].AsBoolean(); data.Contribution = UserGroupMemberInfo["Contribution"].AsInteger(); data.ListInProfile = UserGroupMemberInfo["ListInProfile"].AsBoolean(); data.ActiveRole = UserGroupMemberInfo["SelectedRoleID"].AsUUID(); /////////////////////////////// // Agent Specific Information: // OSDMap UserActiveGroup; if (SimianGetGenericEntry(agentID, "Group", "ActiveGroup", out UserActiveGroup)) { data.Active = UserActiveGroup["GroupID"].AsUUID().Equals(groupID); } /////////////////////////////// // Role Specific Information: // OSDMap GroupRoleInfo; if (SimianGetGenericEntry(groupID, "GroupRole", data.ActiveRole.ToString(), out GroupRoleInfo)) { data.GroupTitle = GroupRoleInfo["Title"].AsString(); data.GroupPowers = GroupRoleInfo["Powers"].AsULong(); } /////////////////////////////// // Group Specific Information: // OSDMap GroupInfo; string GroupName; if (SimianGetFirstGenericEntry(groupID, "Group", out GroupName, out GroupInfo)) { data.GroupID = groupID; data.AllowPublish = GroupInfo["AllowPublish"].AsBoolean(); data.Charter = GroupInfo["Charter"].AsString(); data.FounderID = GroupInfo["FounderID"].AsUUID(); data.GroupName = GroupName; data.GroupPicture = GroupInfo["InsigniaID"].AsUUID(); data.MaturePublish = GroupInfo["MaturePublish"].AsBoolean(); data.MembershipFee = GroupInfo["MembershipFee"].AsInteger(); data.OpenEnrollment = GroupInfo["OpenEnrollment"].AsBoolean(); data.ShowInList = GroupInfo["ShowInList"].AsBoolean(); } } return data; } public GroupMembershipData GetAgentActiveMembership(UUID requestingAgentID, UUID agentID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); UUID GroupID = UUID.Zero; OSDMap UserActiveGroup; if (SimianGetGenericEntry(agentID, "Group", "ActiveGroup", out UserActiveGroup)) { GroupID = UserActiveGroup["GroupID"].AsUUID(); } if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Active GroupID : {0}", GroupID.ToString()); return GetAgentGroupMembership(requestingAgentID, agentID, GroupID); } public List GetAgentGroupMemberships(UUID requestingAgentID, UUID agentID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List memberships = new List(); Dictionary GroupMemberShips; if (SimianGetGenericEntries(agentID, "GroupMember", out GroupMemberShips)) { foreach (string key in GroupMemberShips.Keys) { memberships.Add(GetAgentGroupMembership(requestingAgentID, agentID, UUID.Parse(key))); } } return memberships; } public List GetAgentGroupRoles(UUID requestingAgentID, UUID agentID, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List Roles = new List(); Dictionary GroupRoles; if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles)) { Dictionary MemberRoles; if (SimianGetGenericEntries(agentID, "GroupRole" + groupID.ToString(), out MemberRoles)) { foreach (KeyValuePair kvp in MemberRoles) { GroupRolesData data = new GroupRolesData(); data.RoleID = UUID.Parse(kvp.Key); data.Name = GroupRoles[kvp.Key]["Name"].AsString(); data.Description = GroupRoles[kvp.Key]["Description"].AsString(); data.Title = GroupRoles[kvp.Key]["Title"].AsString(); data.Powers = GroupRoles[kvp.Key]["Powers"].AsULong(); Roles.Add(data); } } } return Roles; } public List GetGroupRoles(UUID requestingAgentID, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List Roles = new List(); Dictionary GroupRoles; if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles)) { foreach (KeyValuePair role in GroupRoles) { GroupRolesData data = new GroupRolesData(); data.RoleID = UUID.Parse(role.Key); data.Name = role.Value["Name"].AsString(); data.Description = role.Value["Description"].AsString(); data.Title = role.Value["Title"].AsString(); data.Powers = role.Value["Powers"].AsULong(); Dictionary GroupRoleMembers; if (SimianGetGenericEntries("GroupRole" + groupID.ToString(), role.Key, out GroupRoleMembers)) { data.Members = GroupRoleMembers.Count; } else { data.Members = 0; } Roles.Add(data); } } return Roles; } public List GetGroupMembers(UUID requestingAgentID, UUID GroupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List members = new List(); OSDMap GroupInfo; string GroupName; UUID GroupOwnerRoleID = UUID.Zero; if (!SimianGetFirstGenericEntry(GroupID, "Group", out GroupName, out GroupInfo)) { return members; } GroupOwnerRoleID = GroupInfo["OwnerRoleID"].AsUUID(); // Locally cache group roles, since we'll be needing this data for each member Dictionary GroupRoles; SimianGetGenericEntries(GroupID, "GroupRole", out GroupRoles); // Locally cache list of group owners Dictionary GroupOwners; SimianGetGenericEntries("GroupRole" + GroupID.ToString(), GroupOwnerRoleID.ToString(), out GroupOwners); Dictionary GroupMembers; if (SimianGetGenericEntries("GroupMember", GroupID.ToString(), out GroupMembers)) { foreach (KeyValuePair member in GroupMembers) { GroupMembersData data = new GroupMembersData(); data.AgentID = member.Key; UUID SelectedRoleID = member.Value["SelectedRoleID"].AsUUID(); data.AcceptNotices = member.Value["AcceptNotices"].AsBoolean(); data.ListInProfile = member.Value["ListInProfile"].AsBoolean(); data.Contribution = member.Value["Contribution"].AsInteger(); data.IsOwner = GroupOwners.ContainsKey(member.Key); OSDMap GroupRoleInfo = GroupRoles[SelectedRoleID.ToString()]; data.Title = GroupRoleInfo["Title"].AsString(); data.AgentPowers = GroupRoleInfo["Powers"].AsULong(); members.Add(data); } } return members; } public List GetGroupRoleMembers(UUID requestingAgentID, UUID groupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List members = new List(); Dictionary GroupRoles; if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles)) { foreach (KeyValuePair Role in GroupRoles) { Dictionary GroupRoleMembers; if (SimianGetGenericEntries("GroupRole"+groupID.ToString(), Role.Key, out GroupRoleMembers)) { foreach (KeyValuePair GroupRoleMember in GroupRoleMembers) { GroupRoleMembersData data = new GroupRoleMembersData(); data.MemberID = GroupRoleMember.Key; data.RoleID = UUID.Parse(Role.Key); members.Add(data); } } } } return members; } public List GetGroupNotices(UUID requestingAgentID, UUID GroupID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); List values = new List(); Dictionary Notices; if (SimianGetGenericEntries(GroupID, "GroupNotice", out Notices)) { foreach (KeyValuePair Notice in Notices) { GroupNoticeData data = new GroupNoticeData(); data.NoticeID = UUID.Parse(Notice.Key); data.Timestamp = Notice.Value["TimeStamp"].AsUInteger(); data.FromName = Notice.Value["FromName"].AsString(); data.Subject = Notice.Value["Subject"].AsString(); data.HasAttachment = Notice.Value["BinaryBucket"].AsBinary().Length > 0; //TODO: Figure out how to get this data.AssetType = 0; values.Add(data); } } return values; } public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap GroupNotice; UUID GroupID; if (SimianGetFirstGenericEntry("GroupNotice", noticeID.ToString(), out GroupID, out GroupNotice)) { GroupNoticeInfo data = new GroupNoticeInfo(); data.GroupID = GroupID; data.Message = GroupNotice["Message"].AsString(); data.BinaryBucket = GroupNotice["BinaryBucket"].AsBinary(); data.noticeData.NoticeID = noticeID; data.noticeData.Timestamp = GroupNotice["TimeStamp"].AsUInteger(); data.noticeData.FromName = GroupNotice["FromName"].AsString(); data.noticeData.Subject = GroupNotice["Subject"].AsString(); data.noticeData.HasAttachment = data.BinaryBucket.Length > 0; data.noticeData.AssetType = 0; if (data.Message == null) { data.Message = string.Empty; } return data; } return null; } public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); OSDMap Notice = new OSDMap(); Notice["TimeStamp"] = OSD.FromUInteger((uint)Util.UnixTimeSinceEpoch()); Notice["FromName"] = OSD.FromString(fromName); Notice["Subject"] = OSD.FromString(subject); Notice["Message"] = OSD.FromString(message); Notice["BinaryBucket"] = OSD.FromBinary(binaryBucket); SimianAddGeneric(groupID, "GroupNotice", noticeID.ToString(), Notice); } #endregion #region GroupSessionTracking public void ResetAgentGroupChatSessions(UUID agentID) { Dictionary agentSessions; if (SimianGetGenericEntries(agentID, "GroupSessionDropped", out agentSessions)) { foreach (string GroupID in agentSessions.Keys) { SimianRemoveGenericEntry(agentID, "GroupSessionDropped", GroupID); } } if (SimianGetGenericEntries(agentID, "GroupSessionInvited", out agentSessions)) { foreach (string GroupID in agentSessions.Keys) { SimianRemoveGenericEntry(agentID, "GroupSessionInvited", GroupID); } } } public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID) { OSDMap session; return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session); } public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID) { SimianAddGeneric(agentID, "GroupSessionDropped", groupID.ToString(), new OSDMap()); } public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID) { SimianAddGeneric(agentID, "GroupSessionInvited", groupID.ToString(), new OSDMap()); } public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID) { OSDMap session; return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session); } #endregion private void EnsureRoleNotSelectedByMember(UUID groupID, UUID roleID, UUID userID) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // If member's SelectedRole is roleID, change their selected role to Everyone // before removing them from the role OSDMap UserGroupInfo; if (SimianGetGenericEntry(userID, "GroupMember", groupID.ToString(), out UserGroupInfo)) { if (UserGroupInfo["SelectedRoleID"].AsUUID() == roleID) { UserGroupInfo["SelectedRoleID"] = OSD.FromUUID(UUID.Zero); } SimianAddGeneric(userID, "GroupMember", groupID.ToString(), UserGroupInfo); } } #region Simian Util Methods private bool SimianAddGeneric(UUID ownerID, string type, string key, OSDMap map) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2},{3})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type, key); string value = OSDParser.SerializeJsonString(map); if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] value: {0}", value); NameValueCollection RequestArgs = new NameValueCollection { { "RequestMethod", "AddGeneric" }, { "OwnerID", ownerID.ToString() }, { "Type", type }, { "Key", key }, { "Value", value} }; OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean()) { return true; } else { m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error {0}, {1}, {2}, {3}", ownerID, type, key, Response["Message"]); return false; } } /// /// Returns the first of possibly many entries for Owner/Type pair /// private bool SimianGetFirstGenericEntry(UUID ownerID, string type, out string key, out OSDMap map) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type); NameValueCollection RequestArgs = new NameValueCollection { { "RequestMethod", "GetGenerics" }, { "OwnerID", ownerID.ToString() }, { "Type", type } }; OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)Response["Entries"]; if (entryArray.Count >= 1) { OSDMap entryMap = entryArray[0] as OSDMap; key = entryMap["Key"].AsString(); map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); return true; } else { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } } else { m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", Response["Message"]); } key = null; map = null; return false; } private bool SimianGetFirstGenericEntry(string type, string key, out UUID ownerID, out OSDMap map) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name, type, key); NameValueCollection RequestArgs = new NameValueCollection { { "RequestMethod", "GetGenerics" }, { "Type", type }, { "Key", key} }; OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)Response["Entries"]; if (entryArray.Count >= 1) { OSDMap entryMap = entryArray[0] as OSDMap; ownerID = entryMap["OwnerID"].AsUUID(); map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); return true; } else { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } } else { m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", Response["Message"]); } ownerID = UUID.Zero; map = null; return false; } private bool SimianGetGenericEntry(UUID ownerID, string type, string key, out OSDMap map) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2},{3})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type, key); NameValueCollection RequestArgs = new NameValueCollection { { "RequestMethod", "GetGenerics" }, { "OwnerID", ownerID.ToString() }, { "Type", type }, { "Key", key} }; OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)Response["Entries"]; if (entryArray.Count == 1) { OSDMap entryMap = entryArray[0] as OSDMap; key = entryMap["Key"].AsString(); map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); return true; } else { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } } else { m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", Response["Message"]); } map = null; return false; } private bool SimianGetGenericEntries(UUID ownerID, string type, out Dictionary maps) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name,ownerID, type); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetGenerics" }, { "OwnerID", ownerID.ToString() }, { "Type", type } }; OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { maps = new Dictionary(); OSDArray entryArray = (OSDArray)response["Entries"]; foreach (OSDMap entryMap in entryArray) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); maps.Add(entryMap["Key"].AsString(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString())); } if (maps.Count == 0) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } return true; } else { maps = null; m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", response["Message"]); } return false; } private bool SimianGetGenericEntries(string type, string key, out Dictionary maps) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name, type, key); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "GetGenerics" }, { "Type", type }, { "Key", key } }; OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { maps = new Dictionary(); OSDArray entryArray = (OSDArray)response["Entries"]; foreach (OSDMap entryMap in entryArray) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); maps.Add(entryMap["OwnerID"].AsUUID(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString())); } if (maps.Count == 0) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } return true; } else { maps = null; m_log.WarnFormat("[SIMIAN-GROUPS-CONNECTOR]: Error retrieving group info ({0})", response["Message"]); } return false; } private bool SimianRemoveGenericEntry(UUID ownerID, string type, string key) { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2},{3})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type, key); NameValueCollection requestArgs = new NameValueCollection { { "RequestMethod", "RemoveGeneric" }, { "OwnerID", ownerID.ToString() }, { "Type", type }, { "Key", key } }; OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean()) { return true; } else { m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error {0}, {1}, {2}, {3}", ownerID, type, key, response["Message"]); return false; } } #endregion #region CheesyCache OSDMap CachedPostRequest(NameValueCollection requestArgs) { // Immediately forward the request if the cache is disabled. if (m_cacheTimeout == 0) { m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: cache is disabled"); return WebUtil.PostToService(m_groupsServerURI, requestArgs); } // Check if this is an update or a request if (requestArgs["RequestMethod"] == "RemoveGeneric" || requestArgs["RequestMethod"] == "AddGeneric") { m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: clearing generics cache"); // Any and all updates cause the cache to clear m_memoryCache.Clear(); // Send update to server, return the response without caching it return WebUtil.PostToService(m_groupsServerURI, requestArgs); } // If we're not doing an update, we must be requesting data // Create the cache key for the request and see if we have it cached string CacheKey = WebUtil.BuildQueryString(requestArgs); // This code uses a leader/follower pattern. On a cache miss, the request is added // to a queue; the first thread to add it to the queue completes the request while // follow on threads busy wait for the results, this situation seems to happen // often when checking permissions while (true) { OSDMap response = null; bool firstRequest = false; lock (m_memoryCache) { if (m_memoryCache.TryGetValue(CacheKey, out response)) return response; if (! m_pendingRequests.ContainsKey(CacheKey)) { m_pendingRequests.Add(CacheKey,true); firstRequest = true; } } if (firstRequest) { // if it wasn't in the cache, pass the request to the Simian Grid Services try { response = WebUtil.PostToService(m_groupsServerURI, requestArgs); } catch (Exception) { m_log.ErrorFormat("[SIMIAN GROUPS CONNECTOR]: request failed {0}", CacheKey); } // and cache the response lock (m_memoryCache) { m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout)); m_pendingRequests.Remove(CacheKey); } return response; } Thread.Sleep(50); // waiting for a web request to complete, 50msecs is reasonable } // if (!m_memoryCache.TryGetValue(CacheKey, out response)) // { // m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: query not in the cache"); // Util.PrintCallStack(); // // if it wasn't in the cache, pass the request to the Simian Grid Services // response = WebUtil.PostToService(m_groupsServerURI, requestArgs); // // and cache the response // m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout)); // } // // return cached response // return response; } #endregion } }