diff --git a/.gitignore b/.gitignore
index fae7509131..91e9be50cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -98,3 +98,5 @@ OpenSim/Region/ScriptEngine/test-results/
OpenSim/Tests/Common/test-results/
OpenSim/Tests/test-results/
test-results/
+doc/html
+doc/doxygen.error.log
diff --git a/.nant/local.include b/.nant/local.include
index 5185717cbc..c20794499f 100644
--- a/.nant/local.include
+++ b/.nant/local.include
@@ -43,10 +43,11 @@
+
-
+
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 43dea0bd3b..8ff55df3da 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -65,6 +65,7 @@ what it is today.
* A_Biondi
* alex_carnell
* Alan Webb (IBM)
+* Allen Kerensky
* BigFootAg
* BlueWall Slade
* brianw/Sir_Ahzz
@@ -154,6 +155,7 @@ what it is today.
* tglion
* tlaukkan/Tommil (Tommi S. E. Laukkanen, Bubble Cloud)
* tyre
+* Vegaslon
* VikingErik
* Vytek
* webmage (IBM)
diff --git a/OpenSim/Addons/Groups/ForeignImporter.cs b/OpenSim/Addons/Groups/ForeignImporter.cs
new file mode 100644
index 0000000000..788d21dfc5
--- /dev/null
+++ b/OpenSim/Addons/Groups/ForeignImporter.cs
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+
+namespace OpenSim.Groups
+{
+ public class ForeignImporter
+ {
+ IUserManagement m_UserManagement;
+ public ForeignImporter(IUserManagement uman)
+ {
+ m_UserManagement = uman;
+ }
+
+ public GroupMembersData ConvertGroupMembersData(ExtendedGroupMembersData _m)
+ {
+ GroupMembersData m = new GroupMembersData();
+ m.AcceptNotices = _m.AcceptNotices;
+ m.AgentPowers = _m.AgentPowers;
+ m.Contribution = _m.Contribution;
+ m.IsOwner = _m.IsOwner;
+ m.ListInProfile = _m.ListInProfile;
+ m.OnlineStatus = _m.OnlineStatus;
+ m.Title = _m.Title;
+
+ string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
+ Util.ParseUniversalUserIdentifier(_m.AgentID, out m.AgentID, out url, out first, out last, out tmp);
+ if (url != string.Empty)
+ m_UserManagement.AddUser(m.AgentID, first, last, url);
+
+ return m;
+ }
+
+ public GroupRoleMembersData ConvertGroupRoleMembersData(ExtendedGroupRoleMembersData _rm)
+ {
+ GroupRoleMembersData rm = new GroupRoleMembersData();
+ rm.RoleID = _rm.RoleID;
+
+ string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
+ Util.ParseUniversalUserIdentifier(_rm.MemberID, out rm.MemberID, out url, out first, out last, out tmp);
+ if (url != string.Empty)
+ m_UserManagement.AddUser(rm.MemberID, first, last, url);
+
+ return rm;
+ }
+
+ }
+}
diff --git a/OpenSim/Addons/Groups/GroupsExtendedData.cs b/OpenSim/Addons/Groups/GroupsExtendedData.cs
new file mode 100644
index 0000000000..6f4db286f4
--- /dev/null
+++ b/OpenSim/Addons/Groups/GroupsExtendedData.cs
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+
+using OpenSim.Framework;
+using OpenMetaverse;
+
+namespace OpenSim.Groups
+{
+ public class ExtendedGroupRecord : GroupRecord
+ {
+ public int MemberCount;
+ public int RoleCount;
+ public string ServiceLocation;
+ public string FounderUUI;
+ }
+
+ public class ExtendedGroupMembershipData : GroupMembershipData
+ {
+ public string AccessToken;
+ }
+
+ public class ExtendedGroupMembersData
+ {
+ // This is the only difference: this is a string
+ public string AgentID;
+ public int Contribution;
+ public string OnlineStatus;
+ public ulong AgentPowers;
+ public string Title;
+ public bool IsOwner;
+ public bool ListInProfile;
+ public bool AcceptNotices;
+ public string AccessToken;
+ }
+
+ public class ExtendedGroupRoleMembersData
+ {
+ public UUID RoleID;
+ // This is the only difference: this is a string
+ public string MemberID;
+
+ }
+
+ public struct ExtendedGroupNoticeData
+ {
+ public UUID NoticeID;
+ public uint Timestamp;
+ public string FromName;
+ public string Subject;
+ public bool HasAttachment;
+ public byte AttachmentType;
+ public string AttachmentName;
+ public UUID AttachmentItemID;
+ public string AttachmentOwnerID;
+
+ public GroupNoticeData ToGroupNoticeData()
+ {
+ GroupNoticeData n = new GroupNoticeData();
+ n.FromName = this.FromName;
+ n.AssetType = this.AttachmentType;
+ n.HasAttachment = this.HasAttachment;
+ n.NoticeID = this.NoticeID;
+ n.Subject = this.Subject;
+ n.Timestamp = this.Timestamp;
+
+ return n;
+ }
+ }
+
+ public class GroupsDataUtils
+ {
+ public static string Sanitize(string s)
+ {
+ return s == null ? string.Empty : s;
+ }
+
+ public static Dictionary GroupRecord(ExtendedGroupRecord grec)
+ {
+ Dictionary dict = new Dictionary();
+ if (grec == null)
+ return dict;
+
+ dict["AllowPublish"] = grec.AllowPublish.ToString();
+ dict["Charter"] = Sanitize(grec.Charter);
+ dict["FounderID"] = grec.FounderID.ToString();
+ dict["FounderUUI"] = Sanitize(grec.FounderUUI);
+ dict["GroupID"] = grec.GroupID.ToString();
+ dict["GroupName"] = Sanitize(grec.GroupName);
+ dict["InsigniaID"] = grec.GroupPicture.ToString();
+ dict["MaturePublish"] = grec.MaturePublish.ToString();
+ dict["MembershipFee"] = grec.MembershipFee.ToString();
+ dict["OpenEnrollment"] = grec.OpenEnrollment.ToString();
+ dict["OwnerRoleID"] = grec.OwnerRoleID.ToString();
+ dict["ServiceLocation"] = Sanitize(grec.ServiceLocation);
+ dict["ShownInList"] = grec.ShowInList.ToString();
+ dict["MemberCount"] = grec.MemberCount.ToString();
+ dict["RoleCount"] = grec.RoleCount.ToString();
+
+ return dict;
+ }
+
+ public static ExtendedGroupRecord GroupRecord(Dictionary dict)
+ {
+ if (dict == null)
+ return null;
+
+ ExtendedGroupRecord grec = new ExtendedGroupRecord();
+ if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null)
+ grec.AllowPublish = bool.Parse(dict["AllowPublish"].ToString());
+
+ if (dict.ContainsKey("Charter") && dict["Charter"] != null)
+ grec.Charter = dict["Charter"].ToString();
+ else
+ grec.Charter = string.Empty;
+
+ if (dict.ContainsKey("FounderID") && dict["FounderID"] != null)
+ grec.FounderID = UUID.Parse(dict["FounderID"].ToString());
+
+ if (dict.ContainsKey("FounderUUI") && dict["FounderUUI"] != null)
+ grec.FounderUUI = dict["FounderUUI"].ToString();
+ else
+ grec.FounderUUI = string.Empty;
+
+ if (dict.ContainsKey("GroupID") && dict["GroupID"] != null)
+ grec.GroupID = UUID.Parse(dict["GroupID"].ToString());
+
+ if (dict.ContainsKey("GroupName") && dict["GroupName"] != null)
+ grec.GroupName = dict["GroupName"].ToString();
+ else
+ grec.GroupName = string.Empty;
+
+ if (dict.ContainsKey("InsigniaID") && dict["InsigniaID"] != null)
+ grec.GroupPicture = UUID.Parse(dict["InsigniaID"].ToString());
+
+ if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null)
+ grec.MaturePublish = bool.Parse(dict["MaturePublish"].ToString());
+
+ if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null)
+ grec.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString());
+
+ if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null)
+ grec.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString());
+
+ if (dict.ContainsKey("OwnerRoleID") && dict["OwnerRoleID"] != null)
+ grec.OwnerRoleID = UUID.Parse(dict["OwnerRoleID"].ToString());
+
+ if (dict.ContainsKey("ServiceLocation") && dict["ServiceLocation"] != null)
+ grec.ServiceLocation = dict["ServiceLocation"].ToString();
+ else
+ grec.GroupName = string.Empty;
+
+ if (dict.ContainsKey("ShownInList") && dict["ShownInList"] != null)
+ grec.ShowInList = bool.Parse(dict["ShownInList"].ToString());
+
+ if (dict.ContainsKey("MemberCount") && dict["MemberCount"] != null)
+ grec.MemberCount = Int32.Parse(dict["MemberCount"].ToString());
+
+ if (dict.ContainsKey("RoleCount") && dict["RoleCount"] != null)
+ grec.RoleCount = Int32.Parse(dict["RoleCount"].ToString());
+
+ return grec;
+ }
+
+ public static Dictionary GroupMembershipData(ExtendedGroupMembershipData membership)
+ {
+ Dictionary dict = new Dictionary();
+ if (membership == null)
+ return dict;
+
+ dict["AcceptNotices"] = membership.AcceptNotices.ToString();
+ dict["AccessToken"] = Sanitize(membership.AccessToken);
+ dict["Active"] = membership.Active.ToString();
+ dict["ActiveRole"] = membership.ActiveRole.ToString();
+ dict["AllowPublish"] = membership.AllowPublish.ToString();
+ dict["Charter"] = Sanitize(membership.Charter);
+ dict["Contribution"] = membership.Contribution.ToString();
+ dict["FounderID"] = membership.FounderID.ToString();
+ dict["GroupID"] = membership.GroupID.ToString();
+ dict["GroupName"] = Sanitize(membership.GroupName);
+ dict["GroupPicture"] = membership.GroupPicture.ToString();
+ dict["GroupPowers"] = membership.GroupPowers.ToString();
+ dict["GroupTitle"] = Sanitize(membership.GroupTitle);
+ dict["ListInProfile"] = membership.ListInProfile.ToString();
+ dict["MaturePublish"] = membership.MaturePublish.ToString();
+ dict["MembershipFee"] = membership.MembershipFee.ToString();
+ dict["OpenEnrollment"] = membership.OpenEnrollment.ToString();
+ dict["ShowInList"] = membership.ShowInList.ToString();
+
+ return dict;
+ }
+
+ public static ExtendedGroupMembershipData GroupMembershipData(Dictionary dict)
+ {
+ if (dict == null)
+ return null;
+
+ ExtendedGroupMembershipData membership = new ExtendedGroupMembershipData();
+
+ if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null)
+ membership.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString());
+
+ if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null)
+ membership.AccessToken = dict["AccessToken"].ToString();
+ else
+ membership.AccessToken = string.Empty;
+
+ if (dict.ContainsKey("Active") && dict["Active"] != null)
+ membership.Active = bool.Parse(dict["Active"].ToString());
+
+ if (dict.ContainsKey("ActiveRole") && dict["ActiveRole"] != null)
+ membership.ActiveRole = UUID.Parse(dict["ActiveRole"].ToString());
+
+ if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null)
+ membership.AllowPublish = bool.Parse(dict["AllowPublish"].ToString());
+
+ if (dict.ContainsKey("Charter") && dict["Charter"] != null)
+ membership.Charter = dict["Charter"].ToString();
+ else
+ membership.Charter = string.Empty;
+
+ if (dict.ContainsKey("Contribution") && dict["Contribution"] != null)
+ membership.Contribution = Int32.Parse(dict["Contribution"].ToString());
+
+ if (dict.ContainsKey("FounderID") && dict["FounderID"] != null)
+ membership.FounderID = UUID.Parse(dict["FounderID"].ToString());
+
+ if (dict.ContainsKey("GroupID") && dict["GroupID"] != null)
+ membership.GroupID = UUID.Parse(dict["GroupID"].ToString());
+
+ if (dict.ContainsKey("GroupName") && dict["GroupName"] != null)
+ membership.GroupName = dict["GroupName"].ToString();
+ else
+ membership.GroupName = string.Empty;
+
+ if (dict.ContainsKey("GroupPicture") && dict["GroupPicture"] != null)
+ membership.GroupPicture = UUID.Parse(dict["GroupPicture"].ToString());
+
+ if (dict.ContainsKey("GroupPowers") && dict["GroupPowers"] != null)
+ membership.GroupPowers = UInt64.Parse(dict["GroupPowers"].ToString());
+
+ if (dict.ContainsKey("GroupTitle") && dict["GroupTitle"] != null)
+ membership.GroupTitle = dict["GroupTitle"].ToString();
+ else
+ membership.GroupTitle = string.Empty;
+
+ if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null)
+ membership.ListInProfile = bool.Parse(dict["ListInProfile"].ToString());
+
+ if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null)
+ membership.MaturePublish = bool.Parse(dict["MaturePublish"].ToString());
+
+ if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null)
+ membership.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString());
+
+ if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null)
+ membership.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString());
+
+ if (dict.ContainsKey("ShowInList") && dict["ShowInList"] != null)
+ membership.ShowInList = bool.Parse(dict["ShowInList"].ToString());
+
+ return membership;
+ }
+
+ public static Dictionary GroupMembersData(ExtendedGroupMembersData member)
+ {
+ Dictionary dict = new Dictionary();
+
+ dict["AcceptNotices"] = member.AcceptNotices.ToString();
+ dict["AccessToken"] = Sanitize(member.AccessToken);
+ dict["AgentID"] = Sanitize(member.AgentID);
+ dict["AgentPowers"] = member.AgentPowers.ToString();
+ dict["Contribution"] = member.Contribution.ToString();
+ dict["IsOwner"] = member.IsOwner.ToString();
+ dict["ListInProfile"] = member.ListInProfile.ToString();
+ dict["OnlineStatus"] = Sanitize(member.OnlineStatus);
+ dict["Title"] = Sanitize(member.Title);
+
+ return dict;
+ }
+
+ public static ExtendedGroupMembersData GroupMembersData(Dictionary dict)
+ {
+ ExtendedGroupMembersData member = new ExtendedGroupMembersData();
+
+ if (dict == null)
+ return member;
+
+ if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null)
+ member.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString());
+
+ if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null)
+ member.AccessToken = Sanitize(dict["AccessToken"].ToString());
+ else
+ member.AccessToken = string.Empty;
+
+ if (dict.ContainsKey("AgentID") && dict["AgentID"] != null)
+ member.AgentID = Sanitize(dict["AgentID"].ToString());
+ else
+ member.AgentID = UUID.Zero.ToString();
+
+ if (dict.ContainsKey("AgentPowers") && dict["AgentPowers"] != null)
+ member.AgentPowers = UInt64.Parse(dict["AgentPowers"].ToString());
+
+ if (dict.ContainsKey("Contribution") && dict["Contribution"] != null)
+ member.Contribution = Int32.Parse(dict["Contribution"].ToString());
+
+ if (dict.ContainsKey("IsOwner") && dict["IsOwner"] != null)
+ member.IsOwner = bool.Parse(dict["IsOwner"].ToString());
+
+ if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null)
+ member.ListInProfile = bool.Parse(dict["ListInProfile"].ToString());
+
+ if (dict.ContainsKey("OnlineStatus") && dict["OnlineStatus"] != null)
+ member.OnlineStatus = Sanitize(dict["OnlineStatus"].ToString());
+ else
+ member.OnlineStatus = string.Empty;
+
+ if (dict.ContainsKey("Title") && dict["Title"] != null)
+ member.Title = Sanitize(dict["Title"].ToString());
+ else
+ member.Title = string.Empty;
+
+ return member;
+ }
+
+ public static Dictionary GroupRolesData(GroupRolesData role)
+ {
+ Dictionary dict = new Dictionary();
+
+ dict["Description"] = Sanitize(role.Description);
+ dict["Members"] = role.Members.ToString();
+ dict["Name"] = Sanitize(role.Name);
+ dict["Powers"] = role.Powers.ToString();
+ dict["RoleID"] = role.RoleID.ToString();
+ dict["Title"] = Sanitize(role.Title);
+
+ return dict;
+ }
+
+ public static GroupRolesData GroupRolesData(Dictionary dict)
+ {
+ GroupRolesData role = new GroupRolesData();
+
+ if (dict == null)
+ return role;
+
+ if (dict.ContainsKey("Description") && dict["Description"] != null)
+ role.Description = Sanitize(dict["Description"].ToString());
+ else
+ role.Description = string.Empty;
+
+ if (dict.ContainsKey("Members") && dict["Members"] != null)
+ role.Members = Int32.Parse(dict["Members"].ToString());
+
+ if (dict.ContainsKey("Name") && dict["Name"] != null)
+ role.Name = Sanitize(dict["Name"].ToString());
+ else
+ role.Name = string.Empty;
+
+ if (dict.ContainsKey("Powers") && dict["Powers"] != null)
+ role.Powers = UInt64.Parse(dict["Powers"].ToString());
+
+ if (dict.ContainsKey("Title") && dict["Title"] != null)
+ role.Title = Sanitize(dict["Title"].ToString());
+ else
+ role.Title = string.Empty;
+
+ if (dict.ContainsKey("RoleID") && dict["RoleID"] != null)
+ role.RoleID = UUID.Parse(dict["RoleID"].ToString());
+
+ return role;
+ }
+
+ public static Dictionary GroupRoleMembersData(ExtendedGroupRoleMembersData rmember)
+ {
+ Dictionary dict = new Dictionary();
+
+ dict["RoleID"] = rmember.RoleID.ToString();
+ dict["MemberID"] = rmember.MemberID;
+ return dict;
+ }
+
+ public static ExtendedGroupRoleMembersData GroupRoleMembersData(Dictionary dict)
+ {
+ ExtendedGroupRoleMembersData rmember = new ExtendedGroupRoleMembersData();
+
+ if (dict.ContainsKey("RoleID") && dict["RoleID"] != null)
+ rmember.RoleID = new UUID(dict["RoleID"].ToString());
+
+ if (dict.ContainsKey("MemberID") && dict["MemberID"] != null)
+ rmember.MemberID = dict["MemberID"].ToString();
+
+ return rmember;
+ }
+
+ public static Dictionary GroupInviteInfo(GroupInviteInfo invite)
+ {
+ Dictionary dict = new Dictionary();
+
+ dict["InviteID"] = invite.InviteID.ToString();
+ dict["GroupID"] = invite.GroupID.ToString();
+ dict["RoleID"] = invite.RoleID.ToString();
+ dict["AgentID"] = invite.AgentID;
+
+ return dict;
+ }
+
+ public static GroupInviteInfo GroupInviteInfo(Dictionary dict)
+ {
+ if (dict == null)
+ return null;
+
+ GroupInviteInfo invite = new GroupInviteInfo();
+
+ invite.InviteID = new UUID(dict["InviteID"].ToString());
+ invite.GroupID = new UUID(dict["GroupID"].ToString());
+ invite.RoleID = new UUID(dict["RoleID"].ToString());
+ invite.AgentID = Sanitize(dict["AgentID"].ToString());
+
+ return invite;
+ }
+
+ public static Dictionary GroupNoticeData(ExtendedGroupNoticeData notice)
+ {
+ Dictionary dict = new Dictionary();
+
+ dict["NoticeID"] = notice.NoticeID.ToString();
+ dict["Timestamp"] = notice.Timestamp.ToString();
+ dict["FromName"] = Sanitize(notice.FromName);
+ dict["Subject"] = Sanitize(notice.Subject);
+ dict["HasAttachment"] = notice.HasAttachment.ToString();
+ dict["AttachmentItemID"] = notice.AttachmentItemID.ToString();
+ dict["AttachmentName"] = Sanitize(notice.AttachmentName);
+ dict["AttachmentType"] = notice.AttachmentType.ToString();
+ dict["AttachmentOwnerID"] = Sanitize(notice.AttachmentOwnerID);
+
+ return dict;
+ }
+
+ public static ExtendedGroupNoticeData GroupNoticeData(Dictionary dict)
+ {
+ ExtendedGroupNoticeData notice = new ExtendedGroupNoticeData();
+
+ if (dict == null)
+ return notice;
+
+ notice.NoticeID = new UUID(dict["NoticeID"].ToString());
+ notice.Timestamp = UInt32.Parse(dict["Timestamp"].ToString());
+ notice.FromName = Sanitize(dict["FromName"].ToString());
+ notice.Subject = Sanitize(dict["Subject"].ToString());
+ notice.HasAttachment = bool.Parse(dict["HasAttachment"].ToString());
+ notice.AttachmentItemID = new UUID(dict["AttachmentItemID"].ToString());
+ notice.AttachmentName = dict["AttachmentName"].ToString();
+ notice.AttachmentType = byte.Parse(dict["AttachmentType"].ToString());
+ notice.AttachmentOwnerID = dict["AttachmentOwnerID"].ToString();
+
+ return notice;
+ }
+
+ public static Dictionary GroupNoticeInfo(GroupNoticeInfo notice)
+ {
+ Dictionary dict = GroupNoticeData(notice.noticeData);
+
+ dict["GroupID"] = notice.GroupID.ToString();
+ dict["Message"] = Sanitize(notice.Message);
+
+ return dict;
+ }
+
+ public static GroupNoticeInfo GroupNoticeInfo(Dictionary dict)
+ {
+ GroupNoticeInfo notice = new GroupNoticeInfo();
+
+ notice.noticeData = GroupNoticeData(dict);
+ notice.GroupID = new UUID(dict["GroupID"].ToString());
+ notice.Message = Sanitize(dict["Message"].ToString());
+
+ return notice;
+ }
+ }
+
+}
diff --git a/OpenSim/Addons/Groups/GroupsMessagingModule.cs b/OpenSim/Addons/Groups/GroupsMessagingModule.cs
new file mode 100644
index 0000000000..d172d48c6c
--- /dev/null
+++ b/OpenSim/Addons/Groups/GroupsMessagingModule.cs
@@ -0,0 +1,594 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using log4net;
+using Mono.Addins;
+using Nini.Config;
+using OpenMetaverse;
+using OpenMetaverse.StructuredData;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Services.Interfaces;
+using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
+
+namespace OpenSim.Groups
+{
+ [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsMessagingModule")]
+ public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private List m_sceneList = new List();
+ private IPresenceService m_presenceService;
+
+ private IMessageTransferModule m_msgTransferModule = null;
+
+ private IGroupsServicesConnector m_groupData = null;
+
+ // Config Options
+ private bool m_groupMessagingEnabled = false;
+ private bool m_debugEnabled = true;
+
+ ///
+ /// If enabled, module only tries to send group IMs to online users by querying cached presence information.
+ ///
+ private bool m_messageOnlineAgentsOnly;
+
+ ///
+ /// Cache for online users.
+ ///
+ ///
+ /// Group ID is key, presence information for online members is value.
+ /// Will only be non-null if m_messageOnlineAgentsOnly = true
+ /// We cache here so that group messages don't constantly have to re-request the online user list to avoid
+ /// attempted expensive sending of messages to offline users.
+ /// The tradeoff is that a user that comes online will not receive messages consistently from all other users
+ /// until caches have updated.
+ /// Therefore, we set the cache expiry to just 20 seconds.
+ ///
+ private ExpiringCache m_usersOnlineCache;
+
+ private int m_usersOnlineCacheExpirySeconds = 20;
+
+ #region Region Module interfaceBase Members
+
+ public void Initialise(IConfigSource config)
+ {
+ IConfig groupsConfig = config.Configs["Groups"];
+
+ if (groupsConfig == null)
+ // Do not run this module by default.
+ return;
+
+ // if groups aren't enabled, we're not needed.
+ // if we're not specified as the connector to use, then we're not wanted
+ if ((groupsConfig.GetBoolean("Enabled", false) == false)
+ || (groupsConfig.GetString("MessagingModule", "") != Name))
+ {
+ m_groupMessagingEnabled = false;
+ return;
+ }
+
+ m_groupMessagingEnabled = groupsConfig.GetBoolean("MessagingEnabled", true);
+
+ if (!m_groupMessagingEnabled)
+ return;
+
+ m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
+
+ if (m_messageOnlineAgentsOnly)
+ m_usersOnlineCache = new ExpiringCache();
+
+ m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true);
+
+ m_log.InfoFormat(
+ "[Groups.Messaging]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
+ m_messageOnlineAgentsOnly, m_debugEnabled);
+ }
+
+ public void AddRegion(Scene scene)
+ {
+ if (!m_groupMessagingEnabled)
+ return;
+
+ scene.RegisterModuleInterface(this);
+ m_sceneList.Add(scene);
+
+ scene.EventManager.OnNewClient += OnNewClient;
+ scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
+ scene.EventManager.OnClientLogin += OnClientLogin;
+ }
+
+ public void RegionLoaded(Scene scene)
+ {
+ if (!m_groupMessagingEnabled)
+ return;
+
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ m_groupData = scene.RequestModuleInterface();
+
+ // No groups module, no groups messaging
+ if (m_groupData == null)
+ {
+ m_log.Error("[Groups.Messaging]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled.");
+ RemoveRegion(scene);
+ return;
+ }
+
+ m_msgTransferModule = scene.RequestModuleInterface();
+
+ // No message transfer module, no groups messaging
+ if (m_msgTransferModule == null)
+ {
+ m_log.Error("[Groups.Messaging]: Could not get MessageTransferModule");
+ RemoveRegion(scene);
+ return;
+ }
+
+ if (m_presenceService == null)
+ m_presenceService = scene.PresenceService;
+
+ }
+
+ public void RemoveRegion(Scene scene)
+ {
+ if (!m_groupMessagingEnabled)
+ return;
+
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ m_sceneList.Remove(scene);
+ scene.EventManager.OnNewClient -= OnNewClient;
+ scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
+ scene.EventManager.OnClientLogin -= OnClientLogin;
+ scene.UnregisterModuleInterface(this);
+ }
+
+ public void Close()
+ {
+ if (!m_groupMessagingEnabled)
+ return;
+
+ if (m_debugEnabled) m_log.Debug("[Groups.Messaging]: Shutting down GroupsMessagingModule module.");
+
+ m_sceneList.Clear();
+
+ m_groupData = null;
+ m_msgTransferModule = null;
+ }
+
+ public Type ReplaceableInterface
+ {
+ get { return null; }
+ }
+
+ public string Name
+ {
+ get { return "Groups Messaging Module V2"; }
+ }
+
+ public void PostInitialise()
+ {
+ // NoOp
+ }
+
+ #endregion
+
+
+ ///
+ /// Not really needed, but does confirm that the group exists.
+ ///
+ public bool StartGroupChatSession(UUID agentID, UUID groupID)
+ {
+ if (m_debugEnabled)
+ m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null);
+
+ if (groupInfo != null)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
+ {
+ List groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID).ToString(), groupID);
+ int groupMembersCount = groupMembers.Count;
+
+ if (m_messageOnlineAgentsOnly)
+ {
+ string[] t1 = groupMembers.ConvertAll(gmd => gmd.AgentID.ToString()).ToArray();
+
+ // We cache in order not to overwhlem the presence service on large grids with many groups. This does
+ // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed.
+ // (assuming this is the same across all grid simulators).
+ PresenceInfo[] onlineAgents;
+ if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
+ {
+ onlineAgents = m_presenceService.GetAgents(t1);
+ m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
+ }
+
+ HashSet onlineAgentsUuidSet = new HashSet();
+ Array.ForEach(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID));
+
+ groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
+
+ // if (m_debugEnabled)
+// m_log.DebugFormat(
+// "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members, {2} online",
+// groupID, groupMembersCount, groupMembers.Count());
+ }
+ else
+ {
+ if (m_debugEnabled)
+ m_log.DebugFormat(
+ "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members",
+ groupID, groupMembers.Count);
+ }
+
+ int requestStartTick = Environment.TickCount;
+
+ foreach (GroupMembersData member in groupMembers)
+ {
+ if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID.ToString(), groupID))
+ {
+ // Don't deliver messages to people who have dropped this session
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} has dropped session, not delivering to them", member.AgentID);
+ continue;
+ }
+
+ // Copy Message
+ GridInstantMessage msg = new GridInstantMessage();
+ msg.imSessionID = groupID.Guid;
+ msg.fromAgentName = im.fromAgentName;
+ msg.message = im.message;
+ msg.dialog = im.dialog;
+ msg.offline = im.offline;
+ msg.ParentEstateID = im.ParentEstateID;
+ msg.Position = im.Position;
+ msg.RegionID = im.RegionID;
+ msg.binaryBucket = im.binaryBucket;
+ msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
+
+ msg.fromAgentID = im.fromAgentID;
+ msg.fromGroup = true;
+
+ msg.toAgentID = member.AgentID.Guid;
+
+ IClientAPI client = GetActiveClient(member.AgentID);
+ if (client == null)
+ {
+ // If they're not local, forward across the grid
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} via Grid", member.AgentID);
+ m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { });
+ }
+ else
+ {
+ // Deliver locally, directly
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name);
+ ProcessMessageFromGroupSession(msg);
+ }
+ }
+
+ // Temporary for assessing how long it still takes to send messages to large online groups.
+ if (m_messageOnlineAgentsOnly)
+ m_log.DebugFormat(
+ "[Groups.Messaging]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms",
+ groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick);
+ }
+
+ #region SimGridEventHandlers
+
+ void OnClientLogin(IClientAPI client)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name);
+ }
+
+ private void OnNewClient(IClientAPI client)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name);
+
+ client.OnInstantMessage += OnInstantMessage;
+ }
+
+ private void OnGridInstantMessage(GridInstantMessage msg)
+ {
+ // The instant message module will only deliver messages of dialog types:
+ // MessageFromAgent, StartTyping, StopTyping, MessageFromObject
+ //
+ // Any other message type will not be delivered to a client by the
+ // Instant Message Module
+
+
+ if (m_debugEnabled)
+ {
+ m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ DebugGridInstantMessage(msg);
+ }
+
+ // Incoming message from a group
+ if ((msg.fromGroup == true) &&
+ ((msg.dialog == (byte)InstantMessageDialog.SessionSend)
+ || (msg.dialog == (byte)InstantMessageDialog.SessionAdd)
+ || (msg.dialog == (byte)InstantMessageDialog.SessionDrop)))
+ {
+ ProcessMessageFromGroupSession(msg);
+ }
+ }
+
+ private void ProcessMessageFromGroupSession(GridInstantMessage msg)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID);
+
+ UUID AgentID = new UUID(msg.fromAgentID);
+ UUID GroupID = new UUID(msg.imSessionID);
+
+ switch (msg.dialog)
+ {
+ case (byte)InstantMessageDialog.SessionAdd:
+ m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
+ break;
+
+ case (byte)InstantMessageDialog.SessionDrop:
+ m_groupData.AgentDroppedFromGroupChatSession(AgentID.ToString(), GroupID);
+ break;
+
+ case (byte)InstantMessageDialog.SessionSend:
+ if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID)
+ && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID.ToString(), GroupID)
+ )
+ {
+ // Agent not in session and hasn't dropped from session
+ // Add them to the session for now, and Invite them
+ m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
+
+ UUID toAgentID = new UUID(msg.toAgentID);
+ IClientAPI activeClient = GetActiveClient(toAgentID);
+ if (activeClient != null)
+ {
+ GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
+ if (groupInfo != null)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Sending chatterbox invite instant message");
+
+ // Force? open the group session dialog???
+ // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg);
+ IEventQueue eq = activeClient.Scene.RequestModuleInterface();
+ eq.ChatterboxInvitation(
+ GroupID
+ , groupInfo.GroupName
+ , new UUID(msg.fromAgentID)
+ , msg.message
+ , new UUID(msg.toAgentID)
+ , msg.fromAgentName
+ , msg.dialog
+ , msg.timestamp
+ , msg.offline == 1
+ , (int)msg.ParentEstateID
+ , msg.Position
+ , 1
+ , new UUID(msg.imSessionID)
+ , msg.fromGroup
+ , OpenMetaverse.Utils.StringToBytes(groupInfo.GroupName)
+ );
+
+ eq.ChatterBoxSessionAgentListUpdates(
+ new UUID(GroupID)
+ , new UUID(msg.fromAgentID)
+ , new UUID(msg.toAgentID)
+ , false //canVoiceChat
+ , false //isModerator
+ , false //text mute
+ );
+ }
+ }
+ }
+ else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID))
+ {
+ // User hasn't dropped, so they're in the session,
+ // maybe we should deliver it.
+ IClientAPI client = GetActiveClient(new UUID(msg.toAgentID));
+ if (client != null)
+ {
+ // Deliver locally, directly
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} locally", client.Name);
+ client.SendInstantMessage(msg);
+ }
+ else
+ {
+ m_log.WarnFormat("[Groups.Messaging]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
+ }
+ }
+ break;
+
+ default:
+ m_log.WarnFormat("[Groups.Messaging]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString());
+ break;
+ }
+ }
+
+ #endregion
+
+
+ #region ClientEvents
+ private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
+ {
+ if (m_debugEnabled)
+ {
+ m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ DebugGridInstantMessage(im);
+ }
+
+ // Start group IM session
+ if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart))
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups.Messaging]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID);
+
+ UUID GroupID = new UUID(im.imSessionID);
+ UUID AgentID = new UUID(im.fromAgentID);
+
+ GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
+
+ if (groupInfo != null)
+ {
+ m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
+
+ ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID);
+
+ IEventQueue queue = remoteClient.Scene.RequestModuleInterface();
+ queue.ChatterBoxSessionAgentListUpdates(
+ GroupID
+ , AgentID
+ , new UUID(im.toAgentID)
+ , false //canVoiceChat
+ , false //isModerator
+ , false //text mute
+ );
+ }
+ }
+
+ // Send a message from locally connected client to a group
+ if ((im.dialog == (byte)InstantMessageDialog.SessionSend))
+ {
+ UUID GroupID = new UUID(im.imSessionID);
+ UUID AgentID = new UUID(im.fromAgentID);
+
+ if (m_debugEnabled)
+ m_log.DebugFormat("[Groups.Messaging]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString());
+
+ //If this agent is sending a message, then they want to be in the session
+ m_groupData.AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
+
+ SendMessageToGroup(im, GroupID);
+ }
+ }
+
+ #endregion
+
+ void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ OSDMap moderatedMap = new OSDMap(4);
+ moderatedMap.Add("voice", OSD.FromBoolean(false));
+
+ OSDMap sessionMap = new OSDMap(4);
+ sessionMap.Add("moderated_mode", moderatedMap);
+ sessionMap.Add("session_name", OSD.FromString(groupName));
+ sessionMap.Add("type", OSD.FromInteger(0));
+ sessionMap.Add("voice_enabled", OSD.FromBoolean(false));
+
+ OSDMap bodyMap = new OSDMap(4);
+ bodyMap.Add("session_id", OSD.FromUUID(groupID));
+ bodyMap.Add("temp_session_id", OSD.FromUUID(groupID));
+ bodyMap.Add("success", OSD.FromBoolean(true));
+ bodyMap.Add("session_info", sessionMap);
+
+ IEventQueue queue = remoteClient.Scene.RequestModuleInterface();
+
+ if (queue != null)
+ {
+ queue.Enqueue(queue.BuildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId);
+ }
+ }
+
+ private void DebugGridInstantMessage(GridInstantMessage im)
+ {
+ // Don't log any normal IMs (privacy!)
+ if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent)
+ {
+ m_log.WarnFormat("[Groups.Messaging]: IM: fromGroup({0})", im.fromGroup ? "True" : "False");
+ m_log.WarnFormat("[Groups.Messaging]: IM: Dialog({0})", ((InstantMessageDialog)im.dialog).ToString());
+ m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentID({0})", im.fromAgentID.ToString());
+ m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentName({0})", im.fromAgentName.ToString());
+ m_log.WarnFormat("[Groups.Messaging]: IM: imSessionID({0})", im.imSessionID.ToString());
+ m_log.WarnFormat("[Groups.Messaging]: IM: message({0})", im.message.ToString());
+ m_log.WarnFormat("[Groups.Messaging]: IM: offline({0})", im.offline.ToString());
+ m_log.WarnFormat("[Groups.Messaging]: IM: toAgentID({0})", im.toAgentID.ToString());
+ m_log.WarnFormat("[Groups.Messaging]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket"));
+ }
+ }
+
+ #region Client Tools
+
+ ///
+ /// Try to find an active IClientAPI reference for agentID giving preference to root connections
+ ///
+ private IClientAPI GetActiveClient(UUID agentID)
+ {
+ if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Looking for local client {0}", agentID);
+
+ IClientAPI child = null;
+
+ // Try root avatar first
+ foreach (Scene scene in m_sceneList)
+ {
+ ScenePresence sp = scene.GetScenePresence(agentID);
+ if (sp != null)
+ {
+ if (!sp.IsChildAgent)
+ {
+ if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Found root agent for client : {0}", sp.ControllingClient.Name);
+ return sp.ControllingClient;
+ }
+ else
+ {
+ if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Found child agent for client : {0}", sp.ControllingClient.Name);
+ child = sp.ControllingClient;
+ }
+ }
+ }
+
+ // If we didn't find a root, then just return whichever child we found, or null if none
+ if (child == null)
+ {
+ if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Could not find local client for agent : {0}", agentID);
+ }
+ else
+ {
+ if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Returning child agent for client : {0}", child.Name);
+ }
+ return child;
+ }
+
+ #endregion
+ }
+}
diff --git a/OpenSim/Addons/Groups/GroupsModule.cs b/OpenSim/Addons/Groups/GroupsModule.cs
new file mode 100644
index 0000000000..10bfa8f852
--- /dev/null
+++ b/OpenSim/Addons/Groups/GroupsModule.cs
@@ -0,0 +1,1467 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Timers;
+using log4net;
+using Mono.Addins;
+using Nini.Config;
+using OpenMetaverse;
+using OpenMetaverse.StructuredData;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Services.Interfaces;
+using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags;
+
+namespace OpenSim.Groups
+{
+ [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsModule")]
+ public class GroupsModule : ISharedRegionModule, IGroupsModule
+ {
+ ///
+ ///
+
+ private static readonly ILog m_log =
+ LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private List m_sceneList = new List();
+
+ private IMessageTransferModule m_msgTransferModule = null;
+
+ private IGroupsServicesConnector m_groupData = null;
+ private IUserManagement m_UserManagement;
+
+ // Configuration settings
+ private bool m_groupsEnabled = false;
+ private bool m_groupNoticesEnabled = true;
+ private bool m_debugEnabled = false;
+ private int m_levelGroupCreate = 0;
+
+ #region Region Module interfaceBase Members
+
+ public void Initialise(IConfigSource config)
+ {
+ IConfig groupsConfig = config.Configs["Groups"];
+
+ if (groupsConfig == null)
+ {
+ // Do not run this module by default.
+ return;
+ }
+ else
+ {
+ m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false);
+ if (!m_groupsEnabled)
+ {
+ return;
+ }
+
+ if (groupsConfig.GetString("Module", "Default") != Name)
+ {
+ m_groupsEnabled = false;
+
+ return;
+ }
+
+ m_log.InfoFormat("[Groups]: Initializing {0}", this.Name);
+
+ m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true);
+ m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false);
+ m_levelGroupCreate = groupsConfig.GetInt("LevelGroupCreate", 0);
+ }
+ }
+
+ public void AddRegion(Scene scene)
+ {
+ if (m_groupsEnabled)
+ {
+ scene.RegisterModuleInterface(this);
+ scene.AddCommand(
+ "debug",
+ this,
+ "debug groups verbose",
+ "debug groups verbose ",
+ "This setting turns on very verbose groups debugging",
+ HandleDebugGroupsVerbose);
+ }
+ }
+
+ private void HandleDebugGroupsVerbose(object modules, string[] args)
+ {
+ if (args.Length < 4)
+ {
+ MainConsole.Instance.Output("Usage: debug groups verbose ");
+ return;
+ }
+
+ bool verbose = false;
+ if (!bool.TryParse(args[3], out verbose))
+ {
+ MainConsole.Instance.Output("Usage: debug groups verbose ");
+ return;
+ }
+
+ m_debugEnabled = verbose;
+
+ MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
+ }
+
+ public void RegionLoaded(Scene scene)
+ {
+ if (!m_groupsEnabled)
+ return;
+
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ scene.EventManager.OnNewClient += OnNewClient;
+ scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
+ // The InstantMessageModule itself doesn't do this,
+ // so lets see if things explode if we don't do it
+ // scene.EventManager.OnClientClosed += OnClientClosed;
+
+ if (m_groupData == null)
+ {
+ m_groupData = scene.RequestModuleInterface();
+
+ // No Groups Service Connector, then nothing works...
+ if (m_groupData == null)
+ {
+ m_groupsEnabled = false;
+ m_log.Error("[Groups]: Could not get IGroupsServicesConnector");
+ RemoveRegion(scene);
+ return;
+ }
+ }
+
+ if (m_msgTransferModule == null)
+ {
+ m_msgTransferModule = scene.RequestModuleInterface();
+
+ // No message transfer module, no notices, group invites, rejects, ejects, etc
+ if (m_msgTransferModule == null)
+ {
+ m_log.Warn("[Groups]: Could not get MessageTransferModule");
+ }
+ }
+
+ if (m_UserManagement == null)
+ {
+ m_UserManagement = scene.RequestModuleInterface();
+ if (m_UserManagement == null)
+ m_log.Warn("[Groups]: Could not get UserManagementModule");
+ }
+
+ lock (m_sceneList)
+ {
+ m_sceneList.Add(scene);
+ }
+
+
+ }
+
+ public void RemoveRegion(Scene scene)
+ {
+ if (!m_groupsEnabled)
+ return;
+
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ scene.EventManager.OnNewClient -= OnNewClient;
+ scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
+
+ lock (m_sceneList)
+ {
+ m_sceneList.Remove(scene);
+ }
+ }
+
+ public void Close()
+ {
+ if (!m_groupsEnabled)
+ return;
+
+ if (m_debugEnabled) m_log.Debug("[Groups]: Shutting down Groups module.");
+ }
+
+ public Type ReplaceableInterface
+ {
+ get { return null; }
+ }
+
+ public string Name
+ {
+ get { return "Groups Module V2"; }
+ }
+
+ public void PostInitialise()
+ {
+ // NoOp
+ }
+
+ #endregion
+
+ #region EventHandlers
+ private void OnNewClient(IClientAPI client)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest;
+ client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest;
+ client.OnDirFindQuery += OnDirFindQuery;
+ client.OnRequestAvatarProperties += OnRequestAvatarProperties;
+
+ // Used for Notices and Group Invites/Accept/Reject
+ client.OnInstantMessage += OnInstantMessage;
+
+ // Send client their groups information.
+ SendAgentGroupDataUpdate(client, client.AgentId);
+ }
+
+ private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ //GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetRequestingAgentID(remoteClient), avatarID).ToArray();
+ GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID);
+ remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups);
+ }
+
+ /*
+ * This becomes very problematic in a shared module. In a shared module you may have more then one
+ * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections.
+ * The OnClientClosed event does not provide anything to indicate which one of those should be closed
+ * nor does it provide what scene it was from so that the specific reference can be looked up.
+ * The InstantMessageModule.cs does not currently worry about unregistering the handles,
+ * and it should be an issue, since it's the client that references us not the other way around
+ * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed
+ private void OnClientClosed(UUID AgentId)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ lock (m_ActiveClients)
+ {
+ if (m_ActiveClients.ContainsKey(AgentId))
+ {
+ IClientAPI client = m_ActiveClients[AgentId];
+ client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
+ client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest;
+ client.OnDirFindQuery -= OnDirFindQuery;
+ client.OnInstantMessage -= OnInstantMessage;
+
+ m_ActiveClients.Remove(AgentId);
+ }
+ else
+ {
+ if (m_debugEnabled) m_log.WarnFormat("[Groups]: Client closed that wasn't registered here.");
+ }
+
+
+ }
+ }
+ */
+
+ void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart)
+ {
+ if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups)
+ {
+ if (m_debugEnabled)
+ m_log.DebugFormat(
+ "[Groups]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})",
+ System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart);
+
+ // TODO: This currently ignores pretty much all the query flags including Mature and sort order
+ remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentIDStr(remoteClient), queryText).ToArray());
+ }
+
+ }
+
+ private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ UUID activeGroupID = UUID.Zero;
+ string activeGroupTitle = string.Empty;
+ string activeGroupName = string.Empty;
+ ulong activeGroupPowers = (ulong)GroupPowers.None;
+
+ GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetRequestingAgentIDStr(remoteClient), dataForAgentID.ToString());
+ if (membership != null)
+ {
+ activeGroupID = membership.GroupID;
+ activeGroupTitle = membership.GroupTitle;
+ activeGroupPowers = membership.GroupPowers;
+ }
+
+ SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle);
+
+ SendScenePresenceUpdate(dataForAgentID, activeGroupTitle);
+ }
+
+ private void HandleUUIDGroupNameRequest(UUID GroupID, IClientAPI remoteClient)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ string GroupName;
+
+ GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null);
+ if (group != null)
+ {
+ GroupName = group.GroupName;
+ }
+ else
+ {
+ GroupName = "Unknown";
+ }
+
+ remoteClient.SendGroupNameReply(GroupID, GroupName);
+ }
+
+ private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ m_log.DebugFormat("[Groups]: IM From {0} to {1} msg {2} type {3}", im.fromAgentID, im.toAgentID, im.message, (InstantMessageDialog)im.dialog);
+ // Group invitations
+ if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
+ {
+ UUID inviteID = new UUID(im.imSessionID);
+ GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID);
+
+ if (inviteInfo == null)
+ {
+ if (m_debugEnabled) m_log.WarnFormat("[Groups]: Received an Invite IM for an invite that does not exist {0}.", inviteID);
+ return;
+ }
+
+ //m_log.DebugFormat("[XXX]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID);
+
+ UUID fromAgentID = new UUID(im.fromAgentID);
+ UUID invitee = UUID.Zero;
+ string tmp = string.Empty;
+ Util.ParseUniversalUserIdentifier(inviteInfo.AgentID, out invitee, out tmp, out tmp, out tmp, out tmp);
+ if ((inviteInfo != null) && (fromAgentID == invitee))
+ {
+ // Accept
+ if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept)
+ {
+ //m_log.DebugFormat("[XXX]: Received an accept invite notice.");
+
+ // and the sessionid is the role
+ string reason = string.Empty;
+ if (!m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), invitee.ToString(), inviteInfo.GroupID, inviteInfo.RoleID, string.Empty, out reason))
+ remoteClient.SendAgentAlertMessage("Unable to add you to the group: " + reason, false);
+ else
+ {
+ GridInstantMessage msg = new GridInstantMessage();
+ msg.imSessionID = UUID.Zero.Guid;
+ msg.fromAgentID = UUID.Zero.Guid;
+ msg.toAgentID = invitee.Guid;
+ msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
+ msg.fromAgentName = "Groups";
+ msg.message = string.Format("You have been added to the group.");
+ msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox;
+ msg.fromGroup = false;
+ msg.offline = (byte)0;
+ msg.ParentEstateID = 0;
+ msg.Position = Vector3.Zero;
+ msg.RegionID = UUID.Zero.Guid;
+ msg.binaryBucket = new byte[0];
+
+ OutgoingInstantMessage(msg, invitee);
+
+ UpdateAllClientsWithGroupInfo(invitee);
+ }
+
+ m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID);
+
+ }
+
+ // Reject
+ if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: Received a reject invite notice.");
+ m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID);
+
+ m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID);
+ }
+ }
+ }
+
+ // Group notices
+ if ((im.dialog == (byte)InstantMessageDialog.GroupNotice))
+ {
+ if (!m_groupNoticesEnabled)
+ {
+ return;
+ }
+
+ UUID GroupID = new UUID(im.toAgentID);
+ if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null) != null)
+ {
+ UUID NoticeID = UUID.Random();
+ string Subject = im.message.Substring(0, im.message.IndexOf('|'));
+ string Message = im.message.Substring(Subject.Length + 1);
+
+ InventoryItemBase item = null;
+ bool hasAttachment = false;
+
+ if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0)
+ {
+ hasAttachment = true;
+ string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
+ binBucket = binBucket.Remove(0, 14).Trim();
+
+ OSD binBucketOSD = OSDParser.DeserializeLLSDXml(binBucket);
+ if (binBucketOSD is OSDMap)
+ {
+ OSDMap binBucketMap = (OSDMap)binBucketOSD;
+
+ UUID itemID = binBucketMap["item_id"].AsUUID();
+ UUID ownerID = binBucketMap["owner_id"].AsUUID();
+ item = new InventoryItemBase(itemID, ownerID);
+ item = m_sceneList[0].InventoryService.GetItem(item);
+ }
+ else
+ m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType());
+ }
+
+ if (m_groupData.AddGroupNotice(GetRequestingAgentIDStr(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message,
+ hasAttachment,
+ (byte)(item == null ? 0 : item.AssetType),
+ item == null ? null : item.Name,
+ item == null ? UUID.Zero : item.ID,
+ item == null ? UUID.Zero.ToString() : item.Owner.ToString()))
+ {
+ if (OnNewGroupNotice != null)
+ {
+ OnNewGroupNotice(GroupID, NoticeID);
+ }
+
+ // Send notice out to everyone that wants notices
+ // Build notice IIM
+ GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
+ foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), GroupID))
+ {
+ if (member.AcceptNotices)
+ {
+ msg.toAgentID = member.AgentID.Guid;
+ OutgoingInstantMessage(msg, member.AgentID);
+ }
+ }
+ }
+ }
+ }
+
+ if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted)
+ {
+ if (im.binaryBucket.Length < 16) // Invalid
+ return;
+
+ //// 16 bytes are the UUID. Maybe.
+ UUID folderID = new UUID(im.binaryBucket, 0);
+ UUID noticeID = new UUID(im.imSessionID);
+
+ GroupNoticeInfo notice = m_groupData.GetGroupNotice(remoteClient.AgentId.ToString(), noticeID);
+ if (notice != null)
+ {
+ UUID giver = new UUID(im.toAgentID);
+ string tmp = string.Empty;
+ Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out giver, out tmp, out tmp, out tmp, out tmp);
+
+ m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId);
+ InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId,
+ giver, notice.noticeData.AttachmentItemID);
+
+ if (itemCopy == null)
+ {
+ remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false);
+ return;
+ }
+
+ remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0);
+ }
+
+ }
+
+ // Interop, received special 210 code for ejecting a group member
+ // this only works within the comms servers domain, and won't work hypergrid
+ // TODO:FIXME: Use a presense server of some kind to find out where the
+ // client actually is, and try contacting that region directly to notify them,
+ // or provide the notification via xmlrpc update queue
+ if ((im.dialog == 210))
+ {
+ // This is sent from the region that the ejectee was ejected from
+ // if it's being delivered here, then the ejectee is here
+ // so we need to send local updates to the agent.
+
+ UUID ejecteeID = new UUID(im.toAgentID);
+
+ im.dialog = (byte)InstantMessageDialog.MessageFromAgent;
+ OutgoingInstantMessage(im, ejecteeID);
+
+ IClientAPI ejectee = GetActiveClient(ejecteeID);
+ if (ejectee != null)
+ {
+ UUID groupID = new UUID(im.imSessionID);
+ ejectee.SendAgentDropGroup(groupID);
+ }
+ }
+ }
+
+ private void OnGridInstantMessage(GridInstantMessage msg)
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // Trigger the above event handler
+ OnInstantMessage(null, msg);
+
+ // If a message from a group arrives here, it may need to be forwarded to a local client
+ if (msg.fromGroup == true)
+ {
+ switch (msg.dialog)
+ {
+ case (byte)InstantMessageDialog.GroupInvitation:
+ case (byte)InstantMessageDialog.GroupNotice:
+ UUID toAgentID = new UUID(msg.toAgentID);
+ IClientAPI localClient = GetActiveClient(toAgentID);
+ if (localClient != null)
+ {
+ localClient.SendInstantMessage(msg);
+ }
+ break;
+ }
+ }
+ }
+
+ #endregion
+
+ #region IGroupsModule Members
+
+ public event NewGroupNotice OnNewGroupNotice;
+
+ public GroupRecord GetGroupRecord(UUID GroupID)
+ {
+ return m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
+ }
+
+ public GroupRecord GetGroupRecord(string name)
+ {
+ return m_groupData.GetGroupRecord(UUID.Zero.ToString(), UUID.Zero, name);
+ }
+
+ public void ActivateGroup(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ m_groupData.SetAgentActiveGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
+
+ // Changing active group changes title, active powers, all kinds of things
+ // anyone who is in any region that can see this client, should probably be
+ // updated with new group info. At a minimum, they should get ScenePresence
+ // updated with new title.
+ UpdateAllClientsWithGroupInfo(remoteClient.AgentId);
+ }
+
+ ///
+ /// Get the Role Titles for an Agent, for a specific group
+ ///
+ public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ List agentRoles = m_groupData.GetAgentGroupRoles(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
+ GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
+
+ List titles = new List();
+ foreach (GroupRolesData role in agentRoles)
+ {
+ GroupTitlesData title = new GroupTitlesData();
+ title.Name = role.Name;
+ if (agentMembership != null)
+ {
+ title.Selected = agentMembership.ActiveRole == role.RoleID;
+ }
+ title.UUID = role.RoleID;
+
+ titles.Add(title);
+ }
+
+ return titles;
+ }
+
+ public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled)
+ m_log.DebugFormat(
+ "[Groups]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name);
+
+ List data = m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), groupID);
+
+ if (m_debugEnabled)
+ {
+ foreach (GroupMembersData member in data)
+ {
+ m_log.DebugFormat("[Groups]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner);
+ }
+ }
+
+ return data;
+
+ }
+
+ public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ List data = m_groupData.GetGroupRoles(GetRequestingAgentIDStr(remoteClient), groupID);
+
+ return data;
+ }
+
+ public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentIDStr(remoteClient), groupID);
+
+ if (m_debugEnabled)
+ {
+ foreach (GroupRoleMembersData member in data)
+ {
+ m_log.DebugFormat("[Groups]: Member({0}) - Role({1})", member.MemberID, member.RoleID);
+ }
+ }
+ return data;
+ }
+
+ public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ GroupProfileData profile = new GroupProfileData();
+
+ // just to get the OwnerRole...
+ ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), groupID, string.Empty);
+ GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
+ if (groupInfo != null)
+ {
+ profile.AllowPublish = groupInfo.AllowPublish;
+ profile.Charter = groupInfo.Charter;
+ profile.FounderID = groupInfo.FounderID;
+ profile.GroupID = groupID;
+ profile.GroupMembershipCount = groupInfo.MemberCount;
+ profile.GroupRolesCount = groupInfo.RoleCount;
+ profile.InsigniaID = groupInfo.GroupPicture;
+ profile.MaturePublish = groupInfo.MaturePublish;
+ profile.MembershipFee = groupInfo.MembershipFee;
+ profile.Money = 0;
+ profile.Name = groupInfo.GroupName;
+ profile.OpenEnrollment = groupInfo.OpenEnrollment;
+ profile.OwnerRole = groupInfo.OwnerRoleID;
+ profile.ShowInList = groupInfo.ShowInList;
+ }
+ if (memberInfo != null)
+ {
+ profile.MemberTitle = memberInfo.GroupTitle;
+ profile.PowersMask = memberInfo.GroupPowers;
+ }
+
+ return profile;
+ }
+
+ public GroupMembershipData[] GetMembershipData(UUID agentID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ return m_groupData.GetAgentGroupMemberships(UUID.Zero.ToString(), agentID.ToString()).ToArray();
+ }
+
+ public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
+ {
+ if (m_debugEnabled)
+ m_log.DebugFormat(
+ "[Groups]: {0} called with groupID={1}, agentID={2}",
+ System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID);
+
+ return m_groupData.GetAgentGroupMembership(UUID.Zero.ToString(), agentID.ToString(), groupID);
+ }
+
+ public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // Note: Permissions checking for modification rights is handled by the Groups Server/Service
+ string reason = string.Empty;
+ if (!m_groupData.UpdateGroup(GetRequestingAgentIDStr(remoteClient), groupID, charter, showInList, insigniaID, membershipFee,
+ openEnrollment, allowPublish, maturePublish, out reason))
+ remoteClient.SendAgentAlertMessage(reason, false);
+ }
+
+ public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile)
+ {
+ // Note: Permissions checking for modification rights is handled by the Groups Server/Service
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ m_groupData.UpdateMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, acceptNotices, listInProfile);
+ }
+
+ public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called in {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Scene.RegionInfo.RegionName);
+
+ if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), UUID.Zero, name) != null)
+ {
+ remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists.");
+ return UUID.Zero;
+ }
+
+ // check user level
+ ScenePresence avatar = null;
+ Scene scene = (Scene)remoteClient.Scene;
+ scene.TryGetScenePresence(remoteClient.AgentId, out avatar);
+
+ if (avatar != null)
+ {
+ if (avatar.UserLevel < m_levelGroupCreate)
+ {
+ remoteClient.SendCreateGroupReply(UUID.Zero, false, String.Format("Insufficient permissions to create a group. Requires level {0}", m_levelGroupCreate));
+ return UUID.Zero;
+ }
+ }
+
+ // check funds
+ // is there is a money module present ?
+ IMoneyModule money = scene.RequestModuleInterface();
+ if (money != null)
+ {
+ // do the transaction, that is if the agent has got sufficient funds
+ if (!money.AmountCovered(remoteClient.AgentId, money.GroupCreationCharge)) {
+ remoteClient.SendCreateGroupReply(UUID.Zero, false, "Insufficient funds to create a group.");
+ return UUID.Zero;
+ }
+ money.ApplyCharge(remoteClient.AgentId, money.GroupCreationCharge, "Group Creation");
+ }
+ string reason = string.Empty;
+ UUID groupID = m_groupData.CreateGroup(remoteClient.AgentId, name, charter, showInList, insigniaID, membershipFee, openEnrollment,
+ allowPublish, maturePublish, remoteClient.AgentId, out reason);
+
+ if (groupID != UUID.Zero)
+ {
+ remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly");
+
+ // Update the founder with new group information.
+ SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
+ }
+ else
+ remoteClient.SendCreateGroupReply(groupID, false, reason);
+
+ return groupID;
+ }
+
+ public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // ToDo: check if agent is a member of group and is allowed to see notices?
+
+ List notices = m_groupData.GetGroupNotices(GetRequestingAgentIDStr(remoteClient), groupID);
+ List os_notices = new List();
+ foreach (ExtendedGroupNoticeData n in notices)
+ {
+ GroupNoticeData osn = n.ToGroupNoticeData();
+ os_notices.Add(osn);
+ }
+
+ return os_notices.ToArray();
+ }
+
+ ///
+ /// Get the title of the agent's current role.
+ ///
+ public string GetGroupTitle(UUID avatarID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ GroupMembershipData membership = m_groupData.GetAgentActiveMembership(UUID.Zero.ToString(), avatarID.ToString());
+ if (membership != null)
+ {
+ return membership.GroupTitle;
+ }
+ return string.Empty;
+ }
+
+ ///
+ /// Change the current Active Group Role for Agent
+ ///
+ public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ m_groupData.SetAgentActiveGroupRole(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, titleRoleID);
+
+ // TODO: Not sure what all is needed here, but if the active group role change is for the group
+ // the client currently has set active, then we need to do a scene presence update too
+ // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID)
+
+ UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient));
+ }
+
+
+ public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // Security Checks are handled in the Groups Service.
+
+ switch ((OpenMetaverse.GroupRoleUpdate)updateType)
+ {
+ case OpenMetaverse.GroupRoleUpdate.Create:
+ string reason = string.Empty;
+ if (!m_groupData.AddGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, UUID.Random(), name, description, title, powers, out reason))
+ remoteClient.SendAgentAlertMessage("Unable to create role: " + reason, false);
+ break;
+
+ case OpenMetaverse.GroupRoleUpdate.Delete:
+ m_groupData.RemoveGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID);
+ break;
+
+ case OpenMetaverse.GroupRoleUpdate.UpdateAll:
+ case OpenMetaverse.GroupRoleUpdate.UpdateData:
+ case OpenMetaverse.GroupRoleUpdate.UpdatePowers:
+ if (m_debugEnabled)
+ {
+ GroupPowers gp = (GroupPowers)powers;
+ m_log.DebugFormat("[Groups]: Role ({0}) updated with Powers ({1}) ({2})", name, powers.ToString(), gp.ToString());
+ }
+ m_groupData.UpdateGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID, name, description, title, powers);
+ break;
+
+ case OpenMetaverse.GroupRoleUpdate.NoUpdate:
+ default:
+ // No Op
+ break;
+
+ }
+
+ // TODO: This update really should send out updates for everyone in the role that just got changed.
+ SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
+ }
+
+ public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+ // Todo: Security check
+
+ switch (changes)
+ {
+ case 0:
+ // Add
+ m_groupData.AddAgentToGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID);
+
+ break;
+ case 1:
+ // Remove
+ m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID);
+
+ break;
+ default:
+ m_log.ErrorFormat("[Groups]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes);
+ break;
+ }
+
+ // TODO: This update really should send out updates for everyone in the role that just got changed.
+ SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
+ }
+
+ public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called for notice {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, groupNoticeID);
+
+ //GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null);
+
+ GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested);
+ //GridInstantMessage msg = new GridInstantMessage();
+ //msg.imSessionID = UUID.Zero.Guid;
+ //msg.fromAgentID = data.GroupID.Guid;
+ //msg.toAgentID = GetRequestingAgentID(remoteClient).Guid;
+ //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
+ //msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName;
+ //msg.message = data.noticeData.Subject + "|" + data.Message;
+ //msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested;
+ //msg.fromGroup = true;
+ //msg.offline = (byte)0;
+ //msg.ParentEstateID = 0;
+ //msg.Position = Vector3.Zero;
+ //msg.RegionID = UUID.Zero.Guid;
+ //msg.binaryBucket = data.BinaryBucket;
+
+ OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient));
+ }
+
+ public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ GridInstantMessage msg = new GridInstantMessage();
+ byte[] bucket;
+
+ msg.imSessionID = groupNoticeID.Guid;
+ msg.toAgentID = agentID.Guid;
+ msg.dialog = dialog;
+ // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice;
+ msg.fromGroup = true;
+ msg.offline = (byte)0;
+ msg.ParentEstateID = 0;
+ msg.Position = Vector3.Zero;
+ msg.RegionID = UUID.Zero.Guid;
+
+ GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID.ToString(), groupNoticeID);
+ if (info != null)
+ {
+ msg.fromAgentID = info.GroupID.Guid;
+ msg.timestamp = info.noticeData.Timestamp;
+ msg.fromAgentName = info.noticeData.FromName;
+ msg.message = info.noticeData.Subject + "|" + info.Message;
+ if (info.noticeData.HasAttachment)
+ {
+ byte[] name = System.Text.Encoding.UTF8.GetBytes(info.noticeData.AttachmentName);
+ bucket = new byte[19 + name.Length];
+ bucket[0] = 1; // has attachment?
+ bucket[1] = info.noticeData.AttachmentType; // attachment type
+ name.CopyTo(bucket, 18);
+ }
+ else
+ {
+ bucket = new byte[19];
+ bucket[0] = 0; // Has att?
+ bucket[1] = 0; // type
+ bucket[18] = 0; // null terminated
+ }
+
+ info.GroupID.ToBytes(bucket, 2);
+ msg.binaryBucket = bucket;
+ }
+ else
+ {
+ m_log.DebugFormat("[Groups]: Group Notice {0} not found, composing empty message.", groupNoticeID);
+ msg.fromAgentID = UUID.Zero.Guid;
+ msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ;
+ msg.fromAgentName = string.Empty;
+ msg.message = string.Empty;
+ msg.binaryBucket = new byte[0];
+ }
+
+ return msg;
+ }
+
+ public void SendAgentGroupDataUpdate(IClientAPI remoteClient)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // Send agent information about his groups
+ SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
+ }
+
+ public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ string reason = string.Empty;
+ // Should check to see if OpenEnrollment, or if there's an outstanding invitation
+ if (m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, UUID.Zero, string.Empty, out reason))
+ {
+
+ remoteClient.SendJoinGroupReply(groupID, true);
+
+ // Should this send updates to everyone in the group?
+ SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
+ }
+ else
+ remoteClient.SendJoinGroupReply(groupID, false);
+ }
+
+ public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
+
+ remoteClient.SendLeaveGroupReply(groupID, true);
+
+ remoteClient.SendAgentDropGroup(groupID);
+
+ // SL sends out notifcations to the group messaging session that the person has left
+ // Should this also update everyone who is in the group?
+ SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
+ }
+
+ public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID)
+ {
+ EjectGroupMember(remoteClient, GetRequestingAgentID(remoteClient), groupID, ejecteeID);
+ }
+
+ public void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // Todo: Security check?
+ m_groupData.RemoveAgentFromGroup(agentID.ToString(), ejecteeID.ToString(), groupID);
+
+ string agentName;
+ RegionInfo regionInfo;
+
+ // remoteClient provided or just agentID?
+ if (remoteClient != null)
+ {
+ agentName = remoteClient.Name;
+ regionInfo = remoteClient.Scene.RegionInfo;
+ remoteClient.SendEjectGroupMemberReply(agentID, groupID, true);
+ }
+ else
+ {
+ IClientAPI client = GetActiveClient(agentID);
+
+ if (client != null)
+ {
+ agentName = client.Name;
+ regionInfo = client.Scene.RegionInfo;
+ client.SendEjectGroupMemberReply(agentID, groupID, true);
+ }
+ else
+ {
+ regionInfo = m_sceneList[0].RegionInfo;
+ UserAccount acc = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID);
+
+ if (acc != null)
+ {
+ agentName = acc.FirstName + " " + acc.LastName;
+ }
+ else
+ {
+ agentName = "Unknown member";
+ }
+ }
+ }
+
+ GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null);
+
+ UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, ejecteeID);
+ if ((groupInfo == null) || (account == null))
+ {
+ return;
+ }
+
+ // Send Message to Ejectee
+ GridInstantMessage msg = new GridInstantMessage();
+
+ msg.imSessionID = UUID.Zero.Guid;
+ msg.fromAgentID = agentID.Guid;
+ // msg.fromAgentID = info.GroupID;
+ msg.toAgentID = ejecteeID.Guid;
+ //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
+ msg.timestamp = 0;
+ msg.fromAgentName = agentName;
+ msg.message = string.Format("You have been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName);
+ msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent;
+ msg.fromGroup = false;
+ msg.offline = (byte)0;
+ msg.ParentEstateID = 0;
+ msg.Position = Vector3.Zero;
+ msg.RegionID = regionInfo.RegionID.Guid;
+ msg.binaryBucket = new byte[0];
+ OutgoingInstantMessage(msg, ejecteeID);
+
+ // Message to ejector
+ // Interop, received special 210 code for ejecting a group member
+ // this only works within the comms servers domain, and won't work hypergrid
+ // TODO:FIXME: Use a presense server of some kind to find out where the
+ // client actually is, and try contacting that region directly to notify them,
+ // or provide the notification via xmlrpc update queue
+
+ msg = new GridInstantMessage();
+ msg.imSessionID = UUID.Zero.Guid;
+ msg.fromAgentID = agentID.Guid;
+ msg.toAgentID = agentID.Guid;
+ msg.timestamp = 0;
+ msg.fromAgentName = agentName;
+ if (account != null)
+ {
+ msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, account.FirstName + " " + account.LastName);
+ }
+ else
+ {
+ msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, "Unknown member");
+ }
+ msg.dialog = (byte)210; //interop
+ msg.fromGroup = false;
+ msg.offline = (byte)0;
+ msg.ParentEstateID = 0;
+ msg.Position = Vector3.Zero;
+ msg.RegionID = regionInfo.RegionID.Guid;
+ msg.binaryBucket = new byte[0];
+ OutgoingInstantMessage(msg, agentID);
+
+
+ // SL sends out messages to everyone in the group
+ // Who all should receive updates and what should they be updated with?
+ UpdateAllClientsWithGroupInfo(ejecteeID);
+ }
+
+ public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID)
+ {
+ InviteGroup(remoteClient, GetRequestingAgentID(remoteClient), groupID, invitedAgentID, roleID);
+ }
+
+ public void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ string agentName = m_UserManagement.GetUserName(agentID);
+ RegionInfo regionInfo = m_sceneList[0].RegionInfo;
+
+ GroupRecord group = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null);
+ if (group == null)
+ {
+ m_log.DebugFormat("[Groups]: No such group {0}", groupID);
+ return;
+ }
+
+ // Todo: Security check, probably also want to send some kind of notification
+ UUID InviteID = UUID.Random();
+
+ if (m_groupData.AddAgentToGroupInvite(agentID.ToString(), InviteID, groupID, roleID, invitedAgentID.ToString()))
+ {
+ if (m_msgTransferModule != null)
+ {
+ Guid inviteUUID = InviteID.Guid;
+
+ GridInstantMessage msg = new GridInstantMessage();
+
+ msg.imSessionID = inviteUUID;
+
+ // msg.fromAgentID = agentID.Guid;
+ msg.fromAgentID = groupID.Guid;
+ msg.toAgentID = invitedAgentID.Guid;
+ //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
+ msg.timestamp = 0;
+ msg.fromAgentName = agentName;
+ msg.message = string.Format("{0} has invited you to join a group called {1}. There is no cost to join this group.", agentName, group.GroupName);
+ msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
+ msg.fromGroup = true;
+ msg.offline = (byte)0;
+ msg.ParentEstateID = 0;
+ msg.Position = Vector3.Zero;
+ msg.RegionID = regionInfo.RegionID.Guid;
+ msg.binaryBucket = new byte[20];
+
+ OutgoingInstantMessage(msg, invitedAgentID);
+ }
+ }
+ }
+
+ #endregion
+
+ #region Client/Update Tools
+
+ ///
+ /// Try to find an active IClientAPI reference for agentID giving preference to root connections
+ ///
+ private IClientAPI GetActiveClient(UUID agentID)
+ {
+ IClientAPI child = null;
+
+ // Try root avatar first
+ foreach (Scene scene in m_sceneList)
+ {
+ ScenePresence sp = scene.GetScenePresence(agentID);
+ if (sp != null)
+ {
+ if (!sp.IsChildAgent)
+ {
+ return sp.ControllingClient;
+ }
+ else
+ {
+ child = sp.ControllingClient;
+ }
+ }
+ }
+
+ // If we didn't find a root, then just return whichever child we found, or null if none
+ return child;
+ }
+
+ ///
+ /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'.
+ ///
+ private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data)
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ OSDArray AgentData = new OSDArray(1);
+ OSDMap AgentDataMap = new OSDMap(1);
+ AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID));
+ AgentData.Add(AgentDataMap);
+
+
+ OSDArray GroupData = new OSDArray(data.Length);
+ OSDArray NewGroupData = new OSDArray(data.Length);
+
+ foreach (GroupMembershipData membership in data)
+ {
+ if (GetRequestingAgentID(remoteClient) != dataForAgentID)
+ {
+ if (!membership.ListInProfile)
+ {
+ // If we're sending group info to remoteclient about another agent,
+ // filter out groups the other agent doesn't want to share.
+ continue;
+ }
+ }
+
+ OSDMap GroupDataMap = new OSDMap(6);
+ OSDMap NewGroupDataMap = new OSDMap(1);
+
+ GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID));
+ GroupDataMap.Add("GroupPowers", OSD.FromULong(membership.GroupPowers));
+ GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices));
+ GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture));
+ GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution));
+ GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName));
+ NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile));
+
+ GroupData.Add(GroupDataMap);
+ NewGroupData.Add(NewGroupDataMap);
+ }
+
+ OSDMap llDataStruct = new OSDMap(3);
+ llDataStruct.Add("AgentData", AgentData);
+ llDataStruct.Add("GroupData", GroupData);
+ llDataStruct.Add("NewGroupData", NewGroupData);
+
+ if (m_debugEnabled)
+ {
+ m_log.InfoFormat("[Groups]: {0}", OSDParser.SerializeJsonString(llDataStruct));
+ }
+
+ IEventQueue queue = remoteClient.Scene.RequestModuleInterface();
+
+ if (queue != null)
+ {
+ queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient));
+ }
+
+ }
+
+ private void SendScenePresenceUpdate(UUID AgentID, string Title)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: Updating scene title for {0} with title: {1}", AgentID, Title);
+
+ ScenePresence presence = null;
+
+ foreach (Scene scene in m_sceneList)
+ {
+ presence = scene.GetScenePresence(AgentID);
+ if (presence != null)
+ {
+ if (presence.Grouptitle != Title)
+ {
+ presence.Grouptitle = Title;
+
+ if (! presence.IsChildAgent)
+ presence.SendAvatarDataToAllAgents();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Send updates to all clients who might be interested in groups data for dataForClientID
+ ///
+ private void UpdateAllClientsWithGroupInfo(UUID dataForClientID)
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // TODO: Probably isn't nessesary to update every client in every scene.
+ // Need to examine client updates and do only what's nessesary.
+ lock (m_sceneList)
+ {
+ foreach (Scene scene in m_sceneList)
+ {
+ scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); });
+ }
+ }
+ }
+
+ ///
+ /// Update remoteClient with group information about dataForAgentID
+ ///
+ private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID)
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name);
+
+ // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
+
+ OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero);
+
+ // Need to send a group membership update to the client
+ // UDP version doesn't seem to behave nicely. But we're going to send it out here
+ // with an empty group membership to hopefully remove groups being displayed due
+ // to the core Groups Stub
+ //remoteClient.SendGroupMembership(new GroupMembershipData[0]);
+
+ GroupMembershipData[] membershipArray = GetProfileListedGroupMemberships(remoteClient, dataForAgentID);
+ SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipArray);
+ //remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipArray);
+ if (remoteClient.AgentId == dataForAgentID)
+ remoteClient.RefreshGroupMembership();
+ }
+
+ ///
+ /// Get a list of groups memberships for the agent that are marked "ListInProfile"
+ /// (unless that agent has a godLike aspect, in which case get all groups)
+ ///
+ ///
+ ///
+ private GroupMembershipData[] GetProfileListedGroupMemberships(IClientAPI requestingClient, UUID dataForAgentID)
+ {
+ List membershipData = m_groupData.GetAgentGroupMemberships(requestingClient.AgentId.ToString(), dataForAgentID.ToString());
+ GroupMembershipData[] membershipArray;
+
+ // cScene and property accessor 'isGod' are in support of the opertions to bypass 'hidden' group attributes for
+ // those with a GodLike aspect.
+ Scene cScene = (Scene)requestingClient.Scene;
+ bool isGod = cScene.Permissions.IsGod(requestingClient.AgentId);
+
+ if (isGod)
+ {
+ membershipArray = membershipData.ToArray();
+ }
+ else
+ {
+ if (requestingClient.AgentId != dataForAgentID)
+ {
+ Predicate showInProfile = delegate(GroupMembershipData membership)
+ {
+ return membership.ListInProfile;
+ };
+
+ membershipArray = membershipData.FindAll(showInProfile).ToArray();
+ }
+ else
+ {
+ membershipArray = membershipData.ToArray();
+ }
+ }
+
+ if (m_debugEnabled)
+ {
+ m_log.InfoFormat("[Groups]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId);
+ foreach (GroupMembershipData membership in membershipArray)
+ {
+ m_log.InfoFormat("[Groups]: {0} :: {1} - {2} - {3}", dataForAgentID, membership.GroupName, membership.GroupTitle, membership.GroupPowers);
+ }
+ }
+
+ return membershipArray;
+ }
+
+
+ private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle)
+ {
+ if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
+ UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, dataForAgentID);
+ string firstname, lastname;
+ if (account != null)
+ {
+ firstname = account.FirstName;
+ lastname = account.LastName;
+ }
+ else
+ {
+ firstname = "Unknown";
+ lastname = "Unknown";
+ }
+
+ remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname,
+ lastname, activeGroupPowers, activeGroupName,
+ activeGroupTitle);
+ }
+
+ #endregion
+
+ #region IM Backed Processes
+
+ private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo)
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
+
+ IClientAPI localClient = GetActiveClient(msgTo);
+ if (localClient != null)
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is local, delivering directly", localClient.Name);
+ localClient.SendInstantMessage(msg);
+ }
+ else if (m_msgTransferModule != null)
+ {
+ if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo);
+ m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[Groups]: Message Sent: {0}", success?"Succeeded":"Failed"); });
+ }
+ }
+
+ public void NotifyChange(UUID groupID)
+ {
+ // Notify all group members of a chnge in group roles and/or
+ // permissions
+ //
+ }
+
+ #endregion
+
+ private string GetRequestingAgentIDStr(IClientAPI client)
+ {
+ return GetRequestingAgentID(client).ToString();
+ }
+
+ private UUID GetRequestingAgentID(IClientAPI client)
+ {
+ UUID requestingAgentID = UUID.Zero;
+ if (client != null)
+ {
+ requestingAgentID = client.AgentId;
+ }
+ return requestingAgentID;
+ }
+
+ }
+
+}
diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs
new file mode 100644
index 0000000000..59fec6fa38
--- /dev/null
+++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+using OpenSim.Framework;
+using OpenSim.Server.Base;
+
+using OpenMetaverse;
+using log4net;
+
+namespace OpenSim.Groups
+{
+ public class GroupsServiceHGConnector
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private string m_ServerURI;
+ private object m_Lock = new object();
+
+ public GroupsServiceHGConnector(string url)
+ {
+ m_ServerURI = url;
+ if (!m_ServerURI.EndsWith("/"))
+ m_ServerURI += "/";
+
+ m_log.DebugFormat("[Groups.HGConnector]: Groups server at {0}", m_ServerURI);
+ }
+
+ public bool CreateProxy(string RequestingAgentID, string AgentID, string accessToken, UUID groupID, string url, string name, out string reason)
+ {
+ reason = string.Empty;
+
+ Dictionary sendData = new Dictionary();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["AgentID"] = AgentID.ToString();
+ sendData["AccessToken"] = accessToken;
+ sendData["GroupID"] = groupID.ToString();
+ sendData["Location"] = url;
+ sendData["Name"] = name;
+ Dictionary ret = MakeRequest("POSTGROUP", sendData);
+
+ if (ret == null)
+ return false;
+
+ if (!ret.ContainsKey("RESULT"))
+ return false;
+
+ if (ret["RESULT"].ToString().ToLower() != "true")
+ {
+ reason = ret["REASON"].ToString();
+ return false;
+ }
+
+ return true;
+
+ }
+
+ public void RemoveAgentFromGroup(string AgentID, UUID GroupID, string token)
+ {
+ Dictionary sendData = new Dictionary();
+ sendData["AgentID"] = AgentID;
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
+ MakeRequest("REMOVEAGENTFROMGROUP", sendData);
+ }
+
+ public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName, string token)
+ {
+ if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty)))
+ return null;
+
+ Dictionary sendData = new Dictionary();
+ if (GroupID != UUID.Zero)
+ sendData["GroupID"] = GroupID.ToString();
+ if (GroupName != null && GroupName != string.Empty)
+ sendData["Name"] = GroupsDataUtils.Sanitize(GroupName);
+
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
+
+ Dictionary ret = MakeRequest("GETGROUP", sendData);
+
+ if (ret == null)
+ return null;
+
+ if (!ret.ContainsKey("RESULT"))
+ return null;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ return null;
+
+ return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]);
+ }
+
+ public List GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
+ {
+ List members = new List();
+
+ Dictionary sendData = new Dictionary();
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
+ Dictionary ret = MakeRequest("GETGROUPMEMBERS", sendData);
+
+ if (ret == null)
+ return members;
+
+ if (!ret.ContainsKey("RESULT"))
+ return members;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ return members;
+ foreach (object v in ((Dictionary)ret["RESULT"]).Values)
+ {
+ ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)v);
+ members.Add(m);
+ }
+
+ return members;
+ }
+
+ public List GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
+ {
+ List roles = new List();
+
+ Dictionary sendData = new Dictionary();
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
+ Dictionary ret = MakeRequest("GETGROUPROLES", sendData);
+
+ if (ret == null)
+ return roles;
+
+ if (!ret.ContainsKey("RESULT"))
+ return roles;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ return roles;
+ foreach (object v in ((Dictionary)ret["RESULT"]).Values)
+ {
+ GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v);
+ roles.Add(m);
+ }
+
+ return roles;
+ }
+
+ public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
+ {
+ List rmembers = new List();
+
+ Dictionary sendData = new Dictionary();
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
+ Dictionary ret = MakeRequest("GETROLEMEMBERS", sendData);
+
+ if (ret == null)
+ return rmembers;
+
+ if (!ret.ContainsKey("RESULT"))
+ return rmembers;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ return rmembers;
+
+ foreach (object v in ((Dictionary)ret["RESULT"]).Values)
+ {
+ ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary)v);
+ rmembers.Add(m);
+ }
+
+ return rmembers;
+ }
+
+ public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
+ bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
+ {
+ Dictionary sendData = new Dictionary();
+ sendData["GroupID"] = groupID.ToString();
+ sendData["NoticeID"] = noticeID.ToString();
+ sendData["FromName"] = GroupsDataUtils.Sanitize(fromName);
+ sendData["Subject"] = GroupsDataUtils.Sanitize(subject);
+ sendData["Message"] = GroupsDataUtils.Sanitize(message);
+ sendData["HasAttachment"] = hasAttachment.ToString();
+ if (hasAttachment)
+ {
+ sendData["AttachmentType"] = attType.ToString();
+ sendData["AttachmentName"] = attName.ToString();
+ sendData["AttachmentItemID"] = attItemID.ToString();
+ sendData["AttachmentOwnerID"] = attOwnerID;
+ }
+ sendData["RequestingAgentID"] = RequestingAgentID;
+
+ Dictionary ret = MakeRequest("ADDNOTICE", sendData);
+
+ if (ret == null)
+ return false;
+
+ if (!ret.ContainsKey("RESULT"))
+ return false;
+
+ if (ret["RESULT"].ToString().ToLower() != "true")
+ return false;
+
+ return true;
+ }
+
+ public bool VerifyNotice(UUID noticeID, UUID groupID)
+ {
+ Dictionary sendData = new Dictionary();
+ sendData["NoticeID"] = noticeID.ToString();
+ sendData["GroupID"] = groupID.ToString();
+ Dictionary ret = MakeRequest("VERIFYNOTICE", sendData);
+
+ if (ret == null)
+ return false;
+
+ if (!ret.ContainsKey("RESULT"))
+ return false;
+
+ if (ret["RESULT"].ToString().ToLower() != "true")
+ return false;
+
+ return true;
+ }
+
+ //
+ //
+ //
+ //
+ //
+
+ #region Make Request
+
+ private Dictionary MakeRequest(string method, Dictionary sendData)
+ {
+ sendData["METHOD"] = method;
+
+ string reply = string.Empty;
+ lock (m_Lock)
+ reply = SynchronousRestFormsRequester.MakeRequest("POST",
+ m_ServerURI + "hg-groups",
+ ServerUtils.BuildQueryString(sendData));
+
+ //m_log.DebugFormat("[XXX]: reply was {0}", reply);
+
+ if (reply == string.Empty || reply == null)
+ return null;
+
+ Dictionary replyData = ServerUtils.ParseXmlResponse(
+ reply);
+
+ return replyData;
+ }
+ #endregion
+
+ }
+}
diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs
new file mode 100644
index 0000000000..f670272662
--- /dev/null
+++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs
@@ -0,0 +1,717 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+using OpenSim.Framework;
+using OpenSim.Framework.Servers;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Services.Interfaces;
+
+using OpenMetaverse;
+using Mono.Addins;
+using log4net;
+using Nini.Config;
+
+namespace OpenSim.Groups
+{
+ [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceHGConnectorModule")]
+ public class GroupsServiceHGConnectorModule : ISharedRegionModule, IGroupsServicesConnector
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private bool m_Enabled = false;
+ private IGroupsServicesConnector m_LocalGroupsConnector;
+ private string m_LocalGroupsServiceLocation;
+ private IUserManagement m_UserManagement;
+ private IOfflineIMService m_OfflineIM;
+ private IMessageTransferModule m_Messaging;
+ private List m_Scenes;
+ private ForeignImporter m_ForeignImporter;
+ private string m_ServiceLocation;
+ private IConfigSource m_Config;
+
+ private Dictionary m_NetworkConnectors = new Dictionary();
+ private RemoteConnectorCacheWrapper m_CacheWrapper; // for caching info of external group services
+
+ #region ISharedRegionModule
+
+ public void Initialise(IConfigSource config)
+ {
+ IConfig groupsConfig = config.Configs["Groups"];
+ if (groupsConfig == null)
+ return;
+
+ if ((groupsConfig.GetBoolean("Enabled", false) == false)
+ || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name))
+ {
+ return;
+ }
+
+ m_Config = config;
+ m_ServiceLocation = groupsConfig.GetString("LocalService", "local"); // local or remote
+ m_LocalGroupsServiceLocation = groupsConfig.GetString("GroupsExternalURI", "http://127.0.0.1");
+ m_Scenes = new List();
+
+ m_Enabled = true;
+
+ m_log.DebugFormat("[Groups]: Initializing {0} with LocalService {1}", this.Name, m_ServiceLocation);
+ }
+
+ public string Name
+ {
+ get { return "Groups HG Service Connector"; }
+ }
+
+ public Type ReplaceableInterface
+ {
+ get { return null; }
+ }
+
+ public void AddRegion(Scene scene)
+ {
+ if (!m_Enabled)
+ return;
+
+ m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName);
+ scene.RegisterModuleInterface(this);
+ m_Scenes.Add(scene);
+
+ scene.EventManager.OnNewClient += OnNewClient;
+ }
+
+ public void RemoveRegion(Scene scene)
+ {
+ if (!m_Enabled)
+ return;
+
+ scene.UnregisterModuleInterface(this);
+ m_Scenes.Remove(scene);
+ }
+
+ public void RegionLoaded(Scene scene)
+ {
+ if (!m_Enabled)
+ return;
+
+ if (m_UserManagement == null)
+ {
+ m_UserManagement = scene.RequestModuleInterface();
+ m_OfflineIM = scene.RequestModuleInterface();
+ m_Messaging = scene.RequestModuleInterface();
+ m_ForeignImporter = new ForeignImporter(m_UserManagement);
+
+ if (m_ServiceLocation.Equals("local"))
+ {
+ m_LocalGroupsConnector = new GroupsServiceLocalConnectorModule(m_Config, m_UserManagement);
+ // Also, if local, create the endpoint for the HGGroupsService
+ new HGGroupsServiceRobustConnector(m_Config, MainServer.Instance, string.Empty,
+ scene.RequestModuleInterface(), scene.RequestModuleInterface());
+
+ }
+ else
+ m_LocalGroupsConnector = new GroupsServiceRemoteConnectorModule(m_Config, m_UserManagement);
+
+ m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement);
+ }
+
+ }
+
+ public void PostInitialise()
+ {
+ }
+
+ public void Close()
+ {
+ }
+
+ #endregion
+
+ private void OnNewClient(IClientAPI client)
+ {
+ client.OnCompleteMovementToRegion += OnCompleteMovementToRegion;
+ }
+
+ void OnCompleteMovementToRegion(IClientAPI client, bool arg2)
+ {
+ object sp = null;
+ if (client.Scene.TryGetScenePresence(client.AgentId, out sp))
+ {
+ if (sp is ScenePresence && ((ScenePresence)sp).PresenceType != PresenceType.Npc)
+ {
+ AgentCircuitData aCircuit = ((ScenePresence)sp).Scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId);
+ if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 &&
+ m_OfflineIM != null && m_Messaging != null)
+ {
+ List ims = m_OfflineIM.GetMessages(aCircuit.AgentID);
+ if (ims != null && ims.Count > 0)
+ foreach (GridInstantMessage im in ims)
+ m_Messaging.SendInstantMessage(im, delegate(bool success) { });
+ }
+ }
+ }
+ }
+
+ #region IGroupsServicesConnector
+
+ public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment,
+ bool allowPublish, bool maturePublish, UUID founderID, out string reason)
+ {
+ m_log.DebugFormat("[Groups]: Creating group {0}", name);
+ reason = string.Empty;
+ if (m_UserManagement.IsLocalGridUser(RequestingAgentID))
+ return m_LocalGroupsConnector.CreateGroup(RequestingAgentID, name, charter, showInList, insigniaID,
+ membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason);
+ else
+ {
+ reason = "Only local grid users are allowed to create a new group";
+ return UUID.Zero;
+ }
+ }
+
+ public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee,
+ bool openEnrollment, bool allowPublish, bool maturePublish, out string reason)
+ {
+ reason = string.Empty;
+ string url = string.Empty;
+ string name = string.Empty;
+ if (IsLocal(groupID, out url, out name))
+ return m_LocalGroupsConnector.UpdateGroup(AgentUUI(RequestingAgentID), groupID, charter, showInList, insigniaID, membershipFee,
+ openEnrollment, allowPublish, maturePublish, out reason);
+ else
+ {
+ reason = "Changes to remote group not allowed. Please go to the group's original world.";
+ return false;
+ }
+ }
+
+ public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName)
+ {
+ string url = string.Empty;
+ string name = string.Empty;
+ if (IsLocal(GroupID, out url, out name))
+ return m_LocalGroupsConnector.GetGroupRecord(AgentUUI(RequestingAgentID), GroupID, GroupName);
+ else if (url != string.Empty)
+ {
+ ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID);
+ string accessToken = string.Empty;
+ if (membership != null)
+ accessToken = membership.AccessToken;
+ else
+ return null;
+
+ GroupsServiceHGConnector c = GetConnector(url);
+ if (c != null)
+ {
+ ExtendedGroupRecord grec = m_CacheWrapper.GetGroupRecord(RequestingAgentID, GroupID, GroupName, delegate
+ {
+ return c.GetGroupRecord(AgentUUIForOutside(RequestingAgentID), GroupID, GroupName, accessToken);
+ });
+
+ if (grec != null)
+ ImportForeigner(grec.FounderUUI);
+ return grec;
+ }
+ }
+
+ return null;
+ }
+
+ public List FindGroups(string RequestingAgentID, string search)
+ {
+ return m_LocalGroupsConnector.FindGroups(AgentUUI(RequestingAgentID), search);
+ }
+
+ public List GetGroupMembers(string RequestingAgentID, UUID GroupID)
+ {
+ string url = string.Empty, gname = string.Empty;
+ if (IsLocal(GroupID, out url, out gname))
+ return m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), GroupID);
+ else if (!string.IsNullOrEmpty(url))
+ {
+ ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID);
+ string accessToken = string.Empty;
+ if (membership != null)
+ accessToken = membership.AccessToken;
+ else
+ return null;
+
+ GroupsServiceHGConnector c = GetConnector(url);
+ if (c != null)
+ {
+ return m_CacheWrapper.GetGroupMembers(RequestingAgentID, GroupID, delegate
+ {
+ return c.GetGroupMembers(AgentUUIForOutside(RequestingAgentID), GroupID, accessToken);
+ });
+
+ }
+ }
+ return new List();
+ }
+
+ public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason)
+ {
+ reason = string.Empty;
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(groupID, out url, out gname))
+ return m_LocalGroupsConnector.AddGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers, out reason);
+ else
+ {
+ reason = "Operation not allowed outside this group's origin world.";
+ return false;
+ }
+ }
+
+ public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(groupID, out url, out gname))
+ return m_LocalGroupsConnector.UpdateGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers);
+ else
+ {
+ return false;
+ }
+
+ }
+
+ public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(groupID, out url, out gname))
+ m_LocalGroupsConnector.RemoveGroupRole(AgentUUI(RequestingAgentID), groupID, roleID);
+ else
+ {
+ return;
+ }
+ }
+
+ public List GetGroupRoles(string RequestingAgentID, UUID groupID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(groupID, out url, out gname))
+ return m_LocalGroupsConnector.GetGroupRoles(AgentUUI(RequestingAgentID), groupID);
+ else if (!string.IsNullOrEmpty(url))
+ {
+ ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID);
+ string accessToken = string.Empty;
+ if (membership != null)
+ accessToken = membership.AccessToken;
+ else
+ return null;
+
+ GroupsServiceHGConnector c = GetConnector(url);
+ if (c != null)
+ {
+ return m_CacheWrapper.GetGroupRoles(RequestingAgentID, groupID, delegate
+ {
+ return c.GetGroupRoles(AgentUUIForOutside(RequestingAgentID), groupID, accessToken);
+ });
+
+ }
+ }
+
+ return new List();
+ }
+
+ public List GetGroupRoleMembers(string RequestingAgentID, UUID groupID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(groupID, out url, out gname))
+ return m_LocalGroupsConnector.GetGroupRoleMembers(AgentUUI(RequestingAgentID), groupID);
+ else if (!string.IsNullOrEmpty(url))
+ {
+ ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID);
+ string accessToken = string.Empty;
+ if (membership != null)
+ accessToken = membership.AccessToken;
+ else
+ return null;
+
+ GroupsServiceHGConnector c = GetConnector(url);
+ if (c != null)
+ {
+ return m_CacheWrapper.GetGroupRoleMembers(RequestingAgentID, groupID, delegate
+ {
+ return c.GetGroupRoleMembers(AgentUUIForOutside(RequestingAgentID), groupID, accessToken);
+ });
+
+ }
+ }
+
+ return new List();
+ }
+
+ public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason)
+ {
+ string url = string.Empty;
+ string name = string.Empty;
+ reason = string.Empty;
+
+ UUID uid = new UUID(AgentID);
+ if (IsLocal(GroupID, out url, out name))
+ {
+ if (m_UserManagement.IsLocalGridUser(uid)) // local user
+ {
+ // normal case: local group, local user
+ return m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason);
+ }
+ else // local group, foreign user
+ {
+ // the user is accepting the invitation, or joining, where the group resides
+ token = UUID.Random().ToString();
+ bool success = m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason);
+
+ if (success)
+ {
+ url = m_UserManagement.GetUserServerURL(uid, "GroupsServerURI");
+ if (url == string.Empty)
+ {
+ reason = "User doesn't have a groups server";
+ return false;
+ }
+
+ GroupsServiceHGConnector c = GetConnector(url);
+ if (c != null)
+ return c.CreateProxy(AgentUUI(RequestingAgentID), AgentID, token, GroupID, m_LocalGroupsServiceLocation, name, out reason);
+ }
+ }
+ }
+ else if (m_UserManagement.IsLocalGridUser(uid)) // local user
+ {
+ // foreign group, local user. She's been added already by the HG service.
+ // Let's just check
+ if (m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID) != null)
+ return true;
+ }
+
+ reason = "Operation not allowed outside this group's origin world";
+ return false;
+ }
+
+
+ public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ string url = string.Empty, name = string.Empty;
+ if (!IsLocal(GroupID, out url, out name) && url != string.Empty)
+ {
+ ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
+ if (membership != null)
+ {
+ GroupsServiceHGConnector c = GetConnector(url);
+ if (c != null)
+ c.RemoveAgentFromGroup(AgentUUIForOutside(AgentID), GroupID, membership.AccessToken);
+ }
+ }
+
+ // remove from local service
+ m_LocalGroupsConnector.RemoveAgentFromGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
+ }
+
+ public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(groupID, out url, out gname))
+ return m_LocalGroupsConnector.AddAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID, groupID, roleID, AgentUUI(agentID));
+ else
+ return false;
+ }
+
+ public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
+ {
+ return m_LocalGroupsConnector.GetAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID); ;
+ }
+
+ public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
+ {
+ m_LocalGroupsConnector.RemoveAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID);
+ }
+
+ public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(GroupID, out url, out gname))
+ m_LocalGroupsConnector.AddAgentToGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID);
+
+ }
+
+ public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(GroupID, out url, out gname))
+ m_LocalGroupsConnector.RemoveAgentFromGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID);
+ }
+
+ public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(GroupID, out url, out gname))
+ return m_LocalGroupsConnector.GetAgentGroupRoles(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
+ else
+ return new List();
+ }
+
+ public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(GroupID, out url, out gname))
+ m_LocalGroupsConnector.SetAgentActiveGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
+ }
+
+ public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID)
+ {
+ return m_LocalGroupsConnector.GetAgentActiveMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID));
+ }
+
+ public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(GroupID, out url, out gname))
+ m_LocalGroupsConnector.SetAgentActiveGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID);
+ }
+
+ public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile)
+ {
+ m_LocalGroupsConnector.UpdateMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, AcceptNotices, ListInProfile);
+ }
+
+ public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(GroupID, out url, out gname))
+ return m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
+ else
+ return null;
+ }
+
+ public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID)
+ {
+ return m_LocalGroupsConnector.GetAgentGroupMemberships(AgentUUI(RequestingAgentID), AgentUUI(AgentID));
+ }
+
+ public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
+ bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
+ {
+ string url = string.Empty, gname = string.Empty;
+
+ if (IsLocal(groupID, out url, out gname))
+ {
+ if (m_LocalGroupsConnector.AddGroupNotice(AgentUUI(RequestingAgentID), groupID, noticeID, fromName, subject, message,
+ hasAttachment, attType, attName, attItemID, AgentUUI(attOwnerID)))
+ {
+ // then send the notice to every grid for which there are members in this group
+ List members = m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), groupID);
+ List urls = new List();
+ foreach (GroupMembersData m in members)
+ {
+ UUID userID = UUID.Zero;
+ if (!m_UserManagement.IsLocalGridUser(m.AgentID))
+ {
+ string gURL = m_UserManagement.GetUserServerURL(m.AgentID, "GroupsServerURI");
+ if (!urls.Contains(gURL))
+ urls.Add(gURL);
+ }
+ }
+
+ // so we have the list of urls to send the notice to
+ // this may take a long time...
+ Util.FireAndForget(delegate
+ {
+ foreach (string u in urls)
+ {
+ GroupsServiceHGConnector c = GetConnector(u);
+ if (c != null)
+ {
+ c.AddNotice(AgentUUIForOutside(RequestingAgentID), groupID, noticeID, fromName, subject, message,
+ hasAttachment, attType, attName, attItemID, AgentUUIForOutside(attOwnerID));
+ }
+ }
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+ else
+ return false;
+ }
+
+ public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID)
+ {
+ GroupNoticeInfo notice = m_LocalGroupsConnector.GetGroupNotice(AgentUUI(RequestingAgentID), noticeID);
+
+ if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null)
+ ImportForeigner(notice.noticeData.AttachmentOwnerID);
+
+ return notice;
+ }
+
+ public List GetGroupNotices(string RequestingAgentID, UUID GroupID)
+ {
+ return m_LocalGroupsConnector.GetGroupNotices(AgentUUI(RequestingAgentID), GroupID);
+ }
+
+ public void ResetAgentGroupChatSessions(string agentID)
+ {
+ }
+
+ public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID)
+ {
+ return false;
+ }
+
+ public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID)
+ {
+ return false;
+ }
+
+ public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID)
+ {
+ }
+
+ public void AgentInvitedToGroupChatSession(string agentID, UUID groupID)
+ {
+ }
+
+ #endregion
+
+ #region hypergrid groups
+
+ private string AgentUUI(string AgentIDStr)
+ {
+ UUID AgentID = UUID.Zero;
+ try
+ {
+ AgentID = new UUID(AgentIDStr);
+ }
+ catch (FormatException)
+ {
+ return AgentID.ToString();
+ }
+
+ if (m_UserManagement.IsLocalGridUser(AgentID))
+ return AgentID.ToString();
+
+ AgentCircuitData agent = null;
+ foreach (Scene scene in m_Scenes)
+ {
+ agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID);
+ if (agent != null)
+ break;
+ }
+ if (agent == null) // oops
+ return AgentID.ToString();
+
+ return Util.ProduceUserUniversalIdentifier(agent);
+ }
+
+ private string AgentUUIForOutside(string AgentIDStr)
+ {
+ UUID AgentID = UUID.Zero;
+ try
+ {
+ AgentID = new UUID(AgentIDStr);
+ }
+ catch (FormatException)
+ {
+ return AgentID.ToString();
+ }
+
+ AgentCircuitData agent = null;
+ foreach (Scene scene in m_Scenes)
+ {
+ agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID);
+ if (agent != null)
+ break;
+ }
+ if (agent == null) // oops
+ return AgentID.ToString();
+
+ return Util.ProduceUserUniversalIdentifier(agent);
+ }
+
+ private UUID ImportForeigner(string uID)
+ {
+ UUID userID = UUID.Zero;
+ string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
+ if (Util.ParseUniversalUserIdentifier(uID, out userID, out url, out first, out last, out tmp))
+ m_UserManagement.AddUser(userID, first, last, url);
+
+ return userID;
+ }
+
+ private bool IsLocal(UUID groupID, out string serviceLocation, out string name)
+ {
+ serviceLocation = string.Empty;
+ name = string.Empty;
+ ExtendedGroupRecord group = m_LocalGroupsConnector.GetGroupRecord(UUID.Zero.ToString(), groupID, string.Empty);
+ if (group == null)
+ {
+ //m_log.DebugFormat("[XXX]: IsLocal? group {0} not found -- no.", groupID);
+ return false;
+ }
+
+ serviceLocation = group.ServiceLocation;
+ name = group.GroupName;
+ bool isLocal = (group.ServiceLocation == string.Empty);
+ //m_log.DebugFormat("[XXX]: IsLocal? {0}", isLocal);
+ return isLocal;
+ }
+
+ private GroupsServiceHGConnector GetConnector(string url)
+ {
+ lock (m_NetworkConnectors)
+ {
+ if (m_NetworkConnectors.ContainsKey(url))
+ return m_NetworkConnectors[url];
+
+ GroupsServiceHGConnector c = new GroupsServiceHGConnector(url);
+ m_NetworkConnectors[url] = c;
+ }
+
+ return m_NetworkConnectors[url];
+ }
+ #endregion
+ }
+}
diff --git a/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs
new file mode 100644
index 0000000000..3584f78c52
--- /dev/null
+++ b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Reflection;
+using System.Text;
+using System.Xml;
+using System.Collections.Generic;
+using System.IO;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Server.Base;
+using OpenSim.Services.Interfaces;
+using OpenSim.Framework.Servers.HttpServer;
+using OpenSim.Server.Handlers.Base;
+using log4net;
+using OpenMetaverse;
+
+namespace OpenSim.Groups
+{
+ public class HGGroupsServiceRobustConnector : ServiceConnector
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private HGGroupsService m_GroupsService;
+ private string m_HomeURI = string.Empty;
+ private string m_ConfigName = "Groups";
+
+ // Called by Robust shell
+ public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) :
+ this(config, server, configName, null, null)
+ {
+ }
+
+ // Called by the sim-bound module
+ public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName, IOfflineIMService im, IUserAccountService users) :
+ base(config, server, configName)
+ {
+ if (configName != String.Empty)
+ m_ConfigName = configName;
+
+ m_log.DebugFormat("[Groups.RobustHGConnector]: Starting with config name {0}", m_ConfigName);
+
+ string homeURI = Util.GetConfigVarFromSections(config, "HomeURI",
+ new string[] { "Startup", "Hypergrid", m_ConfigName}, string.Empty);
+ if (homeURI == string.Empty)
+ throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide the HomeURI [Startup] or in section {0}", m_ConfigName));
+
+ IConfig cnf = config.Configs[m_ConfigName];
+ if (cnf == null)
+ throw new Exception(String.Format("[Groups.RobustHGConnector]: {0} section does not exist", m_ConfigName));
+
+ if (im == null)
+ {
+ string imDll = cnf.GetString("OfflineIMService", string.Empty);
+ if (imDll == string.Empty)
+ throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide OfflineIMService in section {0}", m_ConfigName));
+
+ Object[] args = new Object[] { config };
+ im = ServerUtils.LoadPlugin(imDll, args);
+ }
+
+ if (users == null)
+ {
+ string usersDll = cnf.GetString("UserAccountService", string.Empty);
+ if (usersDll == string.Empty)
+ throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide UserAccountService in section {0}", m_ConfigName));
+
+ Object[] args = new Object[] { config };
+ users = ServerUtils.LoadPlugin(usersDll, args);
+ }
+
+ m_GroupsService = new HGGroupsService(config, im, users, homeURI);
+
+ server.AddStreamHandler(new HGGroupsServicePostHandler(m_GroupsService));
+ }
+
+ }
+
+ public class HGGroupsServicePostHandler : BaseStreamHandler
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private HGGroupsService m_GroupsService;
+
+ public HGGroupsServicePostHandler(HGGroupsService service) :
+ base("POST", "/hg-groups")
+ {
+ m_GroupsService = service;
+ }
+
+ public override byte[] Handle(string path, Stream requestData,
+ IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+ {
+ StreamReader sr = new StreamReader(requestData);
+ string body = sr.ReadToEnd();
+ sr.Close();
+ body = body.Trim();
+
+ //m_log.DebugFormat("[XXX]: query String: {0}", body);
+
+ try
+ {
+ Dictionary request =
+ ServerUtils.ParseQueryString(body);
+
+ if (!request.ContainsKey("METHOD"))
+ return FailureResult();
+
+ string method = request["METHOD"].ToString();
+ request.Remove("METHOD");
+
+ m_log.DebugFormat("[Groups.RobustHGConnector]: {0}", method);
+ switch (method)
+ {
+ case "POSTGROUP":
+ return HandleAddGroupProxy(request);
+ case "REMOVEAGENTFROMGROUP":
+ return HandleRemoveAgentFromGroup(request);
+ case "GETGROUP":
+ return HandleGetGroup(request);
+ case "ADDNOTICE":
+ return HandleAddNotice(request);
+ case "VERIFYNOTICE":
+ return HandleVerifyNotice(request);
+ case "GETGROUPMEMBERS":
+ return HandleGetGroupMembers(request);
+ case "GETGROUPROLES":
+ return HandleGetGroupRoles(request);
+ case "GETROLEMEMBERS":
+ return HandleGetRoleMembers(request);
+
+ }
+ m_log.DebugFormat("[Groups.RobustHGConnector]: unknown method request: {0}", method);
+ }
+ catch (Exception e)
+ {
+ m_log.DebugFormat("[Groups.RobustHGConnector]: Exception {0}", e.StackTrace);
+ }
+
+ return FailureResult();
+ }
+
+ byte[] HandleAddGroupProxy(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")
+ || !request.ContainsKey("AgentID")
+ || !request.ContainsKey("AccessToken") || !request.ContainsKey("Location"))
+ NullResult(result, "Bad network data");
+
+ else
+ {
+ string RequestingAgentID = request["RequestingAgentID"].ToString();
+ string agentID = request["AgentID"].ToString();
+ UUID groupID = new UUID(request["GroupID"].ToString());
+ string accessToken = request["AccessToken"].ToString();
+ string location = request["Location"].ToString();
+ string name = string.Empty;
+ if (request.ContainsKey("Name"))
+ name = request["Name"].ToString();
+
+ string reason = string.Empty;
+ bool success = m_GroupsService.CreateGroupProxy(RequestingAgentID, agentID, accessToken, groupID, location, name, out reason);
+ result["REASON"] = reason;
+ result["RESULT"] = success.ToString();
+ }
+
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ byte[] HandleRemoveAgentFromGroup(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("AccessToken") || !request.ContainsKey("AgentID") ||
+ !request.ContainsKey("GroupID"))
+ NullResult(result, "Bad network data");
+ else
+ {
+ UUID groupID = new UUID(request["GroupID"].ToString());
+ string agentID = request["AgentID"].ToString();
+ string token = request["AccessToken"].ToString();
+ string reason = string.Empty;
+
+ m_GroupsService.RemoveAgentFromGroup(agentID, agentID, groupID, token);
+ }
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ result["RESULT"] = "true";
+ return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
+ }
+
+ byte[] HandleGetGroup(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AccessToken"))
+ NullResult(result, "Bad network data");
+ else
+ {
+ string RequestingAgentID = request["RequestingAgentID"].ToString();
+ string token = request["AccessToken"].ToString();
+
+ UUID groupID = UUID.Zero;
+ string groupName = string.Empty;
+
+ if (request.ContainsKey("GroupID"))
+ groupID = new UUID(request["GroupID"].ToString());
+ if (request.ContainsKey("Name"))
+ groupName = request["Name"].ToString();
+
+ ExtendedGroupRecord grec = m_GroupsService.GetGroupRecord(RequestingAgentID, groupID, groupName, token);
+ if (grec == null)
+ NullResult(result, "Group not found");
+ else
+ result["RESULT"] = GroupsDataUtils.GroupRecord(grec);
+ }
+
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ byte[] HandleGetGroupMembers(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken"))
+ NullResult(result, "Bad network data");
+ else
+ {
+ UUID groupID = new UUID(request["GroupID"].ToString());
+ string requestingAgentID = request["RequestingAgentID"].ToString();
+ string token = request["AccessToken"].ToString();
+
+ List members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID, token);
+ if (members == null || (members != null && members.Count == 0))
+ {
+ NullResult(result, "No members");
+ }
+ else
+ {
+ Dictionary dict = new Dictionary();
+ int i = 0;
+ foreach (ExtendedGroupMembersData m in members)
+ {
+ dict["m-" + i++] = GroupsDataUtils.GroupMembersData(m);
+ }
+
+ result["RESULT"] = dict;
+ }
+ }
+
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ byte[] HandleGetGroupRoles(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken"))
+ NullResult(result, "Bad network data");
+ else
+ {
+ UUID groupID = new UUID(request["GroupID"].ToString());
+ string requestingAgentID = request["RequestingAgentID"].ToString();
+ string token = request["AccessToken"].ToString();
+
+ List roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID, token);
+ if (roles == null || (roles != null && roles.Count == 0))
+ {
+ NullResult(result, "No members");
+ }
+ else
+ {
+ Dictionary dict = new Dictionary();
+ int i = 0;
+ foreach (GroupRolesData r in roles)
+ dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r);
+
+ result["RESULT"] = dict;
+ }
+ }
+
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ byte[] HandleGetRoleMembers(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken"))
+ NullResult(result, "Bad network data");
+ else
+ {
+ UUID groupID = new UUID(request["GroupID"].ToString());
+ string requestingAgentID = request["RequestingAgentID"].ToString();
+ string token = request["AccessToken"].ToString();
+
+ List rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID, token);
+ if (rmembers == null || (rmembers != null && rmembers.Count == 0))
+ {
+ NullResult(result, "No members");
+ }
+ else
+ {
+ Dictionary dict = new Dictionary();
+ int i = 0;
+ foreach (ExtendedGroupRoleMembersData rm in rmembers)
+ dict["rm-" + i++] = GroupsDataUtils.GroupRoleMembersData(rm);
+
+ result["RESULT"] = dict;
+ }
+ }
+
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ byte[] HandleAddNotice(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("NoticeID") ||
+ !request.ContainsKey("FromName") || !request.ContainsKey("Subject") || !request.ContainsKey("Message") ||
+ !request.ContainsKey("HasAttachment"))
+ NullResult(result, "Bad network data");
+
+ else
+ {
+
+ bool hasAtt = bool.Parse(request["HasAttachment"].ToString());
+ byte attType = 0;
+ string attName = string.Empty;
+ string attOwner = string.Empty;
+ UUID attItem = UUID.Zero;
+ if (request.ContainsKey("AttachmentType"))
+ attType = byte.Parse(request["AttachmentType"].ToString());
+ if (request.ContainsKey("AttachmentName"))
+ attName = request["AttachmentType"].ToString();
+ if (request.ContainsKey("AttachmentItemID"))
+ attItem = new UUID(request["AttachmentItemID"].ToString());
+ if (request.ContainsKey("AttachmentOwnerID"))
+ attOwner = request["AttachmentOwnerID"].ToString();
+
+ bool success = m_GroupsService.AddNotice(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()),
+ new UUID(request["NoticeID"].ToString()), request["FromName"].ToString(), request["Subject"].ToString(),
+ request["Message"].ToString(), hasAtt, attType, attName, attItem, attOwner);
+
+ result["RESULT"] = success.ToString();
+ }
+
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ byte[] HandleVerifyNotice(Dictionary request)
+ {
+ Dictionary result = new Dictionary();
+
+ if (!request.ContainsKey("NoticeID") || !request.ContainsKey("GroupID"))
+ NullResult(result, "Bad network data");
+
+ else
+ {
+ UUID noticeID = new UUID(request["NoticeID"].ToString());
+ UUID groupID = new UUID(request["GroupID"].ToString());
+
+ bool success = m_GroupsService.VerifyNotice(noticeID, groupID);
+ //m_log.DebugFormat("[XXX]: VerifyNotice returned {0}", success);
+ result["RESULT"] = success.ToString();
+ }
+
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+
+ //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ //
+ //
+ //
+ //
+ //
+
+ #region Helpers
+
+ private void NullResult(Dictionary result, string reason)
+ {
+ result["RESULT"] = "NULL";
+ result["REASON"] = reason;
+ }
+
+ private byte[] FailureResult()
+ {
+ Dictionary result = new Dictionary();
+ NullResult(result, "Unknown method");
+ string xmlString = ServerUtils.BuildXmlResponse(result);
+ return Util.UTF8NoBomEncoding.GetBytes(xmlString);
+ }
+
+ #endregion
+ }
+}
diff --git a/OpenSim/Addons/Groups/IGroupsServicesConnector.cs b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs
new file mode 100644
index 0000000000..73deb7a7b4
--- /dev/null
+++ b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using OpenMetaverse;
+using OpenSim.Framework;
+
+namespace OpenSim.Groups
+{
+ public interface IGroupsServicesConnector
+ {
+ UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee,
+ bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID, out string reason);
+ bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee,
+ bool openEnrollment, bool allowPublish, bool maturePublish, out string reason);
+ ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName);
+ List FindGroups(string RequestingAgentID, string search);
+ List GetGroupMembers(string RequestingAgentID, UUID GroupID);
+
+ bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason);
+ bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers);
+ void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID);
+ List GetGroupRoles(string RequestingAgentID, UUID GroupID);
+ List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID);
+
+ bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason);
+ void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID);
+
+ bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID);
+ GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID);
+ void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID);
+
+ void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID);
+ void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID);
+ List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID);
+
+ void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID);
+ ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID);
+
+ void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID);
+ void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile);
+
+ ///
+ /// Get information about a specific group to which the user belongs.
+ ///
+ /// The agent requesting the information.
+ /// The agent requested.
+ /// The group requested.
+ ///
+ /// If the user is a member of the group then the data structure is returned. If not, then null is returned.
+ ///
+ ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID);
+
+ ///
+ /// Get information about the groups to which a user belongs.
+ ///
+ /// The agent requesting the information.
+ /// The agent requested.
+ ///
+ /// Information about the groups to which the user belongs. If the user belongs to no groups then an empty
+ /// list is returned.
+ ///
+ List GetAgentGroupMemberships(string RequestingAgentID, string AgentID);
+
+ bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
+ bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID);
+ GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID);
+ List GetGroupNotices(string RequestingAgentID, UUID GroupID);
+
+ void ResetAgentGroupChatSessions(string agentID);
+ bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID);
+ bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID);
+ void AgentDroppedFromGroupChatSession(string agentID, UUID groupID);
+ void AgentInvitedToGroupChatSession(string agentID, UUID groupID);
+
+ }
+
+ public class GroupInviteInfo
+ {
+ public UUID GroupID = UUID.Zero;
+ public UUID RoleID = UUID.Zero;
+ public string AgentID = string.Empty;
+ public UUID InviteID = UUID.Zero;
+ }
+
+ public class GroupNoticeInfo
+ {
+ public ExtendedGroupNoticeData noticeData = new ExtendedGroupNoticeData();
+ public UUID GroupID = UUID.Zero;
+ public string Message = string.Empty;
+ }
+
+}
diff --git a/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs
new file mode 100644
index 0000000000..905bc913ff
--- /dev/null
+++ b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.Framework.Interfaces;
+
+using OpenMetaverse;
+using Mono.Addins;
+using log4net;
+using Nini.Config;
+
+namespace OpenSim.Groups
+{
+ [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceLocalConnectorModule")]
+ public class GroupsServiceLocalConnectorModule : ISharedRegionModule, IGroupsServicesConnector
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private bool m_Enabled = false;
+ private GroupsService m_GroupsService;
+ private IUserManagement m_UserManagement;
+ private List m_Scenes;
+ private ForeignImporter m_ForeignImporter;
+
+ #region constructors
+ public GroupsServiceLocalConnectorModule()
+ {
+ }
+
+ public GroupsServiceLocalConnectorModule(IConfigSource config, IUserManagement uman)
+ {
+ Init(config);
+ m_UserManagement = uman;
+ m_ForeignImporter = new ForeignImporter(uman);
+ }
+ #endregion
+
+ private void Init(IConfigSource config)
+ {
+ m_GroupsService = new GroupsService(config);
+ m_Scenes = new List();
+ }
+
+ #region ISharedRegionModule
+
+ public void Initialise(IConfigSource config)
+ {
+ IConfig groupsConfig = config.Configs["Groups"];
+ if (groupsConfig == null)
+ return;
+
+ if ((groupsConfig.GetBoolean("Enabled", false) == false)
+ || (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name))
+ {
+ return;
+ }
+
+ Init(config);
+ m_Enabled = true;
+
+ m_log.DebugFormat("[Groups]: Initializing {0}", this.Name);
+ }
+
+ public string Name
+ {
+ get { return "Groups Local Service Connector"; }
+ }
+
+ public Type ReplaceableInterface
+ {
+ get { return null; }
+ }
+
+ public void AddRegion(Scene scene)
+ {
+ if (!m_Enabled)
+ return;
+
+ m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName);
+ scene.RegisterModuleInterface(this);
+ m_Scenes.Add(scene);
+ }
+
+ public void RemoveRegion(Scene scene)
+ {
+ if (!m_Enabled)
+ return;
+
+ scene.UnregisterModuleInterface(this);
+ m_Scenes.Remove(scene);
+ }
+
+ public void RegionLoaded(Scene scene)
+ {
+ if (!m_Enabled)
+ return;
+
+ if (m_UserManagement == null)
+ {
+ m_UserManagement = scene.RequestModuleInterface();
+ m_ForeignImporter = new ForeignImporter(m_UserManagement);
+ }
+ }
+
+ public void PostInitialise()
+ {
+ }
+
+ public void Close()
+ {
+ }
+
+ #endregion
+
+ #region IGroupsServicesConnector
+
+ public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment,
+ bool allowPublish, bool maturePublish, UUID founderID, out string reason)
+ {
+ m_log.DebugFormat("[Groups]: Creating group {0}", name);
+ reason = string.Empty;
+ return m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID,
+ membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason);
+ }
+
+ public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee,
+ bool openEnrollment, bool allowPublish, bool maturePublish, out string reason)
+ {
+ reason = string.Empty;
+ m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
+ return true;
+ }
+
+ public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName)
+ {
+ if (GroupID != UUID.Zero)
+ return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID);
+ else if (GroupName != null)
+ return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupName);
+
+ return null;
+ }
+
+ public List FindGroups(string RequestingAgentID, string search)
+ {
+ return m_GroupsService.FindGroups(RequestingAgentID, search);
+ }
+
+ public List GetGroupMembers(string RequestingAgentID, UUID GroupID)
+ {
+ List _members = m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID);
+ if (_members != null && _members.Count > 0)
+ {
+ List members = _members.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData));
+ return members;
+ }
+
+ return new List();
+ }
+
+ public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason)
+ {
+ return m_GroupsService.AddGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, out reason);
+ }
+
+ public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers)
+ {
+ return m_GroupsService.UpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers);
+ }
+
+ public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID)
+ {
+ m_GroupsService.RemoveGroupRole(RequestingAgentID, groupID, roleID);
+ }
+
+ public List GetGroupRoles(string RequestingAgentID, UUID GroupID)
+ {
+ return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID);
+ }
+
+ public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID)
+ {
+ List _rm = m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID);
+ if (_rm != null && _rm.Count > 0)
+ {
+ List rm = _rm.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupRoleMembersData));
+ return rm;
+ }
+
+ return new List();
+
+ }
+
+ public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason)
+ {
+ return m_GroupsService.AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, token, out reason);
+ }
+
+ public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ m_GroupsService.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
+ }
+
+ public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID)
+ {
+ return m_GroupsService.AddAgentToGroupInvite(RequestingAgentID, inviteID, groupID, roleID, agentID);
+ }
+
+ public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
+ {
+ return m_GroupsService.GetAgentToGroupInvite(RequestingAgentID, inviteID); ;
+ }
+
+ public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
+ {
+ m_GroupsService.RemoveAgentToGroupInvite(RequestingAgentID, inviteID);
+ }
+
+ public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
+ {
+ m_GroupsService.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
+ }
+
+ public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
+ {
+ m_GroupsService.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
+ }
+
+ public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ return m_GroupsService.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID);
+ }
+
+ public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ m_GroupsService.SetAgentActiveGroup(RequestingAgentID, AgentID, GroupID);
+ }
+
+ public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID)
+ {
+ return m_GroupsService.GetAgentActiveMembership(RequestingAgentID, AgentID);
+ }
+
+ public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
+ {
+ m_GroupsService.SetAgentActiveGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
+ }
+
+ public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile)
+ {
+ m_GroupsService.UpdateMembership(RequestingAgentID, AgentID, GroupID, AcceptNotices, ListInProfile);
+ }
+
+ public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ return m_GroupsService.GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID); ;
+ }
+
+ public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID)
+ {
+ return m_GroupsService.GetAgentGroupMemberships(RequestingAgentID, AgentID);
+ }
+
+ public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
+ bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
+ {
+ return m_GroupsService.AddGroupNotice(RequestingAgentID, groupID, noticeID, fromName, subject, message,
+ hasAttachment, attType, attName, attItemID, attOwnerID);
+ }
+
+ public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID)
+ {
+ GroupNoticeInfo notice = m_GroupsService.GetGroupNotice(RequestingAgentID, noticeID);
+
+ //if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null)
+ //{
+ // UUID userID = UUID.Zero;
+ // string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
+ // Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out userID, out url, out first, out last, out tmp);
+ // if (url != string.Empty)
+ // m_UserManagement.AddUser(userID, first, last, url);
+ //}
+
+ return notice;
+ }
+
+ public List GetGroupNotices(string RequestingAgentID, UUID GroupID)
+ {
+ return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID);
+ }
+
+ public void ResetAgentGroupChatSessions(string agentID)
+ {
+ }
+
+ public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID)
+ {
+ return false;
+ }
+
+ public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID)
+ {
+ return false;
+ }
+
+ public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID)
+ {
+ }
+
+ public void AgentInvitedToGroupChatSession(string agentID, UUID groupID)
+ {
+ }
+
+ #endregion
+ }
+}
diff --git a/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..5ccd7fea1b
--- /dev/null
+++ b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Mono.Addins;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OpenSim.Addons.Groups")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("http://opensimulator.org")]
+[assembly: AssemblyProduct("OpenSim.Addons.Groups")]
+[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("313d4865-d179-4735-9b5a-fe74885878b2")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("0.7.6.*")]
+
+[assembly: Addin("OpenSim.Groups", "0.1")]
+[assembly: AddinDependency("OpenSim", "0.5")]
diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs
new file mode 100644
index 0000000000..04328c9c3a
--- /dev/null
+++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+using OpenSim.Framework;
+using OpenSim.Server.Base;
+
+using OpenMetaverse;
+using log4net;
+
+namespace OpenSim.Groups
+{
+ public class GroupsServiceRemoteConnector
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private string m_ServerURI;
+ private object m_Lock = new object();
+
+ public GroupsServiceRemoteConnector(string url)
+ {
+ m_ServerURI = url;
+ if (!m_ServerURI.EndsWith("/"))
+ m_ServerURI += "/";
+
+ m_log.DebugFormat("[Groups.RemoteConnector]: Groups server at {0}", m_ServerURI);
+ }
+
+ public ExtendedGroupRecord CreateGroup(string RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment,
+ bool allowPublish, bool maturePublish, UUID founderID, out string reason)
+ {
+ reason = string.Empty;
+
+ ExtendedGroupRecord rec = new ExtendedGroupRecord();
+ rec.AllowPublish = allowPublish;
+ rec.Charter = charter;
+ rec.FounderID = founderID;
+ rec.GroupName = name;
+ rec.GroupPicture = insigniaID;
+ rec.MaturePublish = maturePublish;
+ rec.MembershipFee = membershipFee;
+ rec.OpenEnrollment = openEnrollment;
+ rec.ShowInList = showInList;
+
+ Dictionary sendData = GroupsDataUtils.GroupRecord(rec);
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["OP"] = "ADD";
+ Dictionary ret = MakeRequest("PUTGROUP", sendData);
+
+ if (ret == null)
+ return null;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ {
+ reason = ret["REASON"].ToString();
+ return null;
+ }
+
+ return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]);
+
+ }
+
+ public ExtendedGroupRecord UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
+ {
+ ExtendedGroupRecord rec = new ExtendedGroupRecord();
+ rec.AllowPublish = allowPublish;
+ rec.Charter = charter;
+ rec.GroupPicture = insigniaID;
+ rec.MaturePublish = maturePublish;
+ rec.GroupID = groupID;
+ rec.MembershipFee = membershipFee;
+ rec.OpenEnrollment = openEnrollment;
+ rec.ShowInList = showInList;
+
+ Dictionary sendData = GroupsDataUtils.GroupRecord(rec);
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["OP"] = "UPDATE";
+ Dictionary ret = MakeRequest("PUTGROUP", sendData);
+
+ if (ret == null || (ret != null && ret["RESULT"].ToString() == "NULL"))
+ return null;
+
+ return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]);
+ }
+
+ public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName)
+ {
+ if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty)))
+ return null;
+
+ Dictionary sendData = new Dictionary();
+ if (GroupID != UUID.Zero)
+ sendData["GroupID"] = GroupID.ToString();
+ if (GroupName != null && GroupName != string.Empty)
+ sendData["Name"] = GroupsDataUtils.Sanitize(GroupName);
+
+ sendData["RequestingAgentID"] = RequestingAgentID;
+
+ Dictionary ret = MakeRequest("GETGROUP", sendData);
+
+ if (ret == null || (ret != null && ret["RESULT"].ToString() == "NULL"))
+ return null;
+
+ return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]);
+ }
+
+ public GroupMembershipData AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason)
+ {
+ reason = string.Empty;
+
+ Dictionary sendData = new Dictionary();
+ sendData["AgentID"] = AgentID;
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["RoleID"] = RoleID.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["AccessToken"] = token;
+ Dictionary ret = MakeRequest("ADDAGENTTOGROUP", sendData);
+
+ if (ret == null)
+ return null;
+
+ if (!ret.ContainsKey("RESULT"))
+ return null;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ {
+ reason = ret["REASON"].ToString();
+ return null;
+ }
+
+ return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]);
+
+ }
+
+ public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ Dictionary sendData = new Dictionary();
+ sendData["AgentID"] = AgentID;
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ MakeRequest("REMOVEAGENTFROMGROUP", sendData);
+ }
+
+ public ExtendedGroupMembershipData GetMembership(string RequestingAgentID, string AgentID, UUID GroupID)
+ {
+ Dictionary sendData = new Dictionary();
+ sendData["AgentID"] = AgentID;
+ if (GroupID != UUID.Zero)
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ Dictionary ret = MakeRequest("GETMEMBERSHIP", sendData);
+
+ if (ret == null)
+ return null;
+
+ if (!ret.ContainsKey("RESULT"))
+ return null;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ return null;
+
+ return GroupsDataUtils.GroupMembershipData((Dictionary)ret["RESULT"]);
+ }
+
+ public List GetMemberships(string RequestingAgentID, string AgentID)
+ {
+ List memberships = new List();
+
+ Dictionary sendData = new Dictionary();
+ sendData["AgentID"] = AgentID;
+ sendData["ALL"] = "true";
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ Dictionary ret = MakeRequest("GETMEMBERSHIP", sendData);
+
+ if (ret == null)
+ return memberships;
+
+ if (!ret.ContainsKey("RESULT"))
+ return memberships;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ return memberships;
+
+ foreach (object v in ((Dictionary)ret["RESULT"]).Values)
+ {
+ GroupMembershipData m = GroupsDataUtils.GroupMembershipData((Dictionary)v);
+ memberships.Add(m);
+ }
+
+ return memberships;
+ }
+
+ public List GetGroupMembers(string RequestingAgentID, UUID GroupID)
+ {
+ List members = new List();
+
+ Dictionary sendData = new Dictionary();
+ sendData["GroupID"] = GroupID.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ Dictionary ret = MakeRequest("GETGROUPMEMBERS", sendData);
+
+ if (ret == null)
+ return members;
+
+ if (!ret.ContainsKey("RESULT"))
+ return members;
+
+ if (ret["RESULT"].ToString() == "NULL")
+ return members;
+ foreach (object v in ((Dictionary)ret["RESULT"]).Values)
+ {
+ ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)v);
+ members.Add(m);
+ }
+
+ return members;
+ }
+
+ public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason)
+ {
+ reason = string.Empty;
+
+ Dictionary sendData = new Dictionary();
+ sendData["GroupID"] = groupID.ToString();
+ sendData["RoleID"] = roleID.ToString();
+ sendData["Name"] = GroupsDataUtils.Sanitize(name);
+ sendData["Description"] = GroupsDataUtils.Sanitize(description);
+ sendData["Title"] = GroupsDataUtils.Sanitize(title);
+ sendData["Powers"] = powers.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["OP"] = "ADD";
+ Dictionary ret = MakeRequest("PUTROLE", sendData);
+
+ if (ret == null)
+ return false;
+
+ if (!ret.ContainsKey("RESULT"))
+ return false;
+
+ if (ret["RESULT"].ToString().ToLower() != "true")
+ {
+ reason = ret["REASON"].ToString();
+ return false;
+ }
+
+ return true;
+ }
+
+ public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers)
+ {
+ Dictionary sendData = new Dictionary();
+ sendData["GroupID"] = groupID.ToString();
+ sendData["RoleID"] = roleID.ToString();
+ sendData["Name"] = GroupsDataUtils.Sanitize(name);
+ sendData["Description"] = GroupsDataUtils.Sanitize(description);
+ sendData["Title"] = GroupsDataUtils.Sanitize(title);
+ sendData["Powers"] = powers.ToString();
+ sendData["RequestingAgentID"] = RequestingAgentID;
+ sendData["OP"] = "UPDATE";
+ Dictionary