362 lines
15 KiB
C#
362 lines
15 KiB
C#
/*
|
|
* Copyright (c) Contributors, http://opensimulator.org/
|
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the OpenSimulator Project nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Timers;
|
|
using log4net;
|
|
using Nini.Config;
|
|
|
|
using OpenMetaverse;
|
|
using OpenSim.Data;
|
|
using OpenSim.Framework;
|
|
using OpenSim.Services.Interfaces;
|
|
|
|
namespace OpenSim.Groups
|
|
{
|
|
public class HGGroupsService : GroupsService
|
|
{
|
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
private IOfflineIMService m_OfflineIM;
|
|
private IUserAccountService m_UserAccounts;
|
|
private string m_HomeURI;
|
|
|
|
public HGGroupsService(IConfigSource config, IOfflineIMService im, IUserAccountService users, string homeURI)
|
|
: base(config, string.Empty)
|
|
{
|
|
m_OfflineIM = im;
|
|
m_UserAccounts = users;
|
|
m_HomeURI = homeURI;
|
|
if (!m_HomeURI.EndsWith("/"))
|
|
m_HomeURI += "/";
|
|
}
|
|
|
|
|
|
#region HG specific operations
|
|
|
|
public bool CreateGroupProxy(string RequestingAgentID, string agentID, string accessToken, UUID groupID, string serviceLocation, string name, out string reason)
|
|
{
|
|
reason = string.Empty;
|
|
Uri uri = null;
|
|
try
|
|
{
|
|
uri = new Uri(serviceLocation);
|
|
}
|
|
catch (UriFormatException)
|
|
{
|
|
reason = "Bad location for group proxy";
|
|
return false;
|
|
}
|
|
|
|
// Check if it already exists
|
|
GroupData grec = m_Database.RetrieveGroup(groupID);
|
|
if (grec == null ||
|
|
(grec != null && grec.Data["Location"] != string.Empty && grec.Data["Location"].ToLower() != serviceLocation.ToLower()))
|
|
{
|
|
// Create the group
|
|
grec = new GroupData();
|
|
grec.GroupID = groupID;
|
|
grec.Data = new Dictionary<string, string>();
|
|
grec.Data["Name"] = name + " @ " + uri.Authority;
|
|
grec.Data["Location"] = serviceLocation;
|
|
grec.Data["Charter"] = string.Empty;
|
|
grec.Data["InsigniaID"] = UUID.Zero.ToString();
|
|
grec.Data["FounderID"] = UUID.Zero.ToString();
|
|
grec.Data["MembershipFee"] = "0";
|
|
grec.Data["OpenEnrollment"] = "0";
|
|
grec.Data["ShowInList"] = "0";
|
|
grec.Data["AllowPublish"] = "0";
|
|
grec.Data["MaturePublish"] = "0";
|
|
grec.Data["OwnerRoleID"] = UUID.Zero.ToString();
|
|
|
|
|
|
if (!m_Database.StoreGroup(grec))
|
|
return false;
|
|
}
|
|
|
|
if (grec.Data["Location"] == string.Empty)
|
|
{
|
|
reason = "Cannot add proxy membership to non-proxy group";
|
|
return false;
|
|
}
|
|
|
|
UUID uid = UUID.Zero;
|
|
string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
|
|
Util.ParseUniversalUserIdentifier(RequestingAgentID, out uid, out url, out first, out last, out tmp);
|
|
string fromName = first + "." + last + "@" + url;
|
|
|
|
// Invite to group again
|
|
InviteToGroup(fromName, groupID, new UUID(agentID), grec.Data["Name"]);
|
|
|
|
// Stick the proxy membership in the DB already
|
|
// we'll delete it if the agent declines the invitation
|
|
MembershipData membership = new MembershipData();
|
|
membership.PrincipalID = agentID;
|
|
membership.GroupID = groupID;
|
|
membership.Data = new Dictionary<string, string>();
|
|
membership.Data["SelectedRoleID"] = UUID.Zero.ToString();
|
|
membership.Data["Contribution"] = "0";
|
|
membership.Data["ListInProfile"] = "1";
|
|
membership.Data["AcceptNotices"] = "1";
|
|
membership.Data["AccessToken"] = accessToken;
|
|
|
|
m_Database.StoreMember(membership);
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, string token)
|
|
{
|
|
// check the token
|
|
MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID);
|
|
if (membership != null)
|
|
{
|
|
if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
|
|
{
|
|
return RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
|
|
}
|
|
else
|
|
{
|
|
m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", AgentID);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token)
|
|
{
|
|
// check the token
|
|
if (!VerifyToken(GroupID, RequestingAgentID, token))
|
|
return null;
|
|
|
|
ExtendedGroupRecord grec;
|
|
if (GroupID == UUID.Zero)
|
|
grec = GetGroupRecord(RequestingAgentID, groupName);
|
|
else
|
|
grec = GetGroupRecord(RequestingAgentID, GroupID);
|
|
|
|
if (grec != null)
|
|
FillFounderUUI(grec);
|
|
|
|
return grec;
|
|
}
|
|
|
|
public List<ExtendedGroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
|
|
{
|
|
if (!VerifyToken(GroupID, RequestingAgentID, token))
|
|
return new List<ExtendedGroupMembersData>();
|
|
|
|
List<ExtendedGroupMembersData> members = GetGroupMembers(RequestingAgentID, GroupID);
|
|
|
|
// convert UUIDs to UUIs
|
|
members.ForEach(delegate (ExtendedGroupMembersData m)
|
|
{
|
|
if (m.AgentID.ToString().Length == 36) // UUID
|
|
{
|
|
UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.AgentID));
|
|
if (account != null)
|
|
m.AgentID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
|
|
}
|
|
});
|
|
|
|
return members;
|
|
}
|
|
|
|
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
|
|
{
|
|
if (!VerifyToken(GroupID, RequestingAgentID, token))
|
|
return new List<GroupRolesData>();
|
|
|
|
return GetGroupRoles(RequestingAgentID, GroupID);
|
|
}
|
|
|
|
public List<ExtendedGroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
|
|
{
|
|
if (!VerifyToken(GroupID, RequestingAgentID, token))
|
|
return new List<ExtendedGroupRoleMembersData>();
|
|
|
|
List<ExtendedGroupRoleMembersData> rolemembers = GetGroupRoleMembers(RequestingAgentID, GroupID);
|
|
|
|
// convert UUIDs to UUIs
|
|
rolemembers.ForEach(delegate(ExtendedGroupRoleMembersData m)
|
|
{
|
|
if (m.MemberID.ToString().Length == 36) // UUID
|
|
{
|
|
UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.MemberID));
|
|
if (account != null)
|
|
m.MemberID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
|
|
}
|
|
});
|
|
|
|
return rolemembers;
|
|
}
|
|
|
|
public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
|
|
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
|
|
{
|
|
// check that the group proxy exists
|
|
ExtendedGroupRecord grec = GetGroupRecord(RequestingAgentID, groupID);
|
|
if (grec == null)
|
|
{
|
|
m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to non-existent group proxy");
|
|
return false;
|
|
}
|
|
|
|
// check that the group is remote
|
|
if (grec.ServiceLocation == string.Empty)
|
|
{
|
|
m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to local (non-proxy) group");
|
|
return false;
|
|
}
|
|
|
|
// check that there isn't already a notice with the same ID
|
|
if (GetGroupNotice(RequestingAgentID, noticeID) != null)
|
|
{
|
|
m_log.DebugFormat("[Groups.HGGroupsService]: a notice with the same ID already exists", grec.ServiceLocation);
|
|
return false;
|
|
}
|
|
|
|
// This has good intentions (security) but it will potentially DDS the origin...
|
|
// We'll need to send a proof along with the message. Maybe encrypt the message
|
|
// using key pairs
|
|
//
|
|
//// check that the notice actually exists in the origin
|
|
//GroupsServiceHGConnector c = new GroupsServiceHGConnector(grec.ServiceLocation);
|
|
//if (!c.VerifyNotice(noticeID, groupID))
|
|
//{
|
|
// m_log.DebugFormat("[Groups.HGGroupsService]: notice does not exist at origin {0}", grec.ServiceLocation);
|
|
// return false;
|
|
//}
|
|
|
|
// ok, we're good!
|
|
return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID);
|
|
}
|
|
|
|
public bool VerifyNotice(UUID noticeID, UUID groupID)
|
|
{
|
|
GroupNoticeInfo notice = GetGroupNotice(string.Empty, noticeID);
|
|
|
|
if (notice == null)
|
|
return false;
|
|
|
|
if (notice.GroupID != groupID)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void InviteToGroup(string fromName, UUID groupID, UUID invitedAgentID, string groupName)
|
|
{
|
|
// Todo: Security check, probably also want to send some kind of notification
|
|
UUID InviteID = UUID.Random();
|
|
|
|
if (AddAgentToGroupInvite(InviteID, groupID, invitedAgentID.ToString()))
|
|
{
|
|
Guid inviteUUID = InviteID.Guid;
|
|
|
|
GridInstantMessage msg = new GridInstantMessage();
|
|
|
|
msg.imSessionID = inviteUUID;
|
|
|
|
// msg.fromAgentID = agentID.Guid;
|
|
msg.fromAgentID = groupID.Guid;
|
|
msg.toAgentID = invitedAgentID.Guid;
|
|
//msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
|
|
msg.timestamp = 0;
|
|
msg.fromAgentName = fromName;
|
|
msg.message = string.Format("Please confirm your acceptance to join group {0}.", groupName);
|
|
msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
|
|
msg.fromGroup = true;
|
|
msg.offline = (byte)0;
|
|
msg.ParentEstateID = 0;
|
|
msg.Position = Vector3.Zero;
|
|
msg.RegionID = UUID.Zero.Guid;
|
|
msg.binaryBucket = new byte[20];
|
|
|
|
string reason = string.Empty;
|
|
m_OfflineIM.StoreMessage(msg, out reason);
|
|
|
|
}
|
|
}
|
|
|
|
private bool AddAgentToGroupInvite(UUID inviteID, UUID groupID, string agentID)
|
|
{
|
|
// Check whether the invitee is already a member of the group
|
|
MembershipData m = m_Database.RetrieveMember(groupID, agentID);
|
|
if (m != null)
|
|
return false;
|
|
|
|
// Check whether there are pending invitations and delete them
|
|
InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID);
|
|
if (invite != null)
|
|
m_Database.DeleteInvite(invite.InviteID);
|
|
|
|
invite = new InvitationData();
|
|
invite.InviteID = inviteID;
|
|
invite.PrincipalID = agentID;
|
|
invite.GroupID = groupID;
|
|
invite.RoleID = UUID.Zero;
|
|
invite.Data = new Dictionary<string, string>();
|
|
|
|
return m_Database.StoreInvitation(invite);
|
|
}
|
|
|
|
private void FillFounderUUI(ExtendedGroupRecord grec)
|
|
{
|
|
UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, grec.FounderID);
|
|
if (account != null)
|
|
grec.FounderUUI = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
|
|
}
|
|
|
|
private bool VerifyToken(UUID groupID, string agentID, string token)
|
|
{
|
|
// check the token
|
|
MembershipData membership = m_Database.RetrieveMember(groupID, agentID);
|
|
if (membership != null)
|
|
{
|
|
if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
|
|
return true;
|
|
else
|
|
m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
|
|
}
|
|
else
|
|
m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", agentID);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|