OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
HGGroupsService.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Timers;
32 using log4net;
33 using Nini.Config;
34 
35 using OpenMetaverse;
36 using OpenSim.Data;
37 using OpenSim.Framework;
38 using OpenSim.Services.Interfaces;
39 
40 namespace OpenSim.Groups
41 {
43  {
44  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 
46  private IOfflineIMService m_OfflineIM;
47  private IUserAccountService m_UserAccounts;
48  private string m_HomeURI;
49 
50  public HGGroupsService(IConfigSource config, IOfflineIMService im, IUserAccountService users, string homeURI)
51  : base(config, string.Empty)
52  {
53  m_OfflineIM = im;
54  m_UserAccounts = users;
55  m_HomeURI = homeURI;
56  if (!m_HomeURI.EndsWith("/"))
57  m_HomeURI += "/";
58  }
59 
60 
61  #region HG specific operations
62 
63  public bool CreateGroupProxy(string RequestingAgentID, string agentID, string accessToken, UUID groupID, string serviceLocation, string name, out string reason)
64  {
65  reason = string.Empty;
66  Uri uri = null;
67  try
68  {
69  uri = new Uri(serviceLocation);
70  }
71  catch (UriFormatException)
72  {
73  reason = "Bad location for group proxy";
74  return false;
75  }
76 
77  // Check if it already exists
78  GroupData grec = m_Database.RetrieveGroup(groupID);
79  if (grec == null ||
80  (grec != null && grec.Data["Location"] != string.Empty && grec.Data["Location"].ToLower() != serviceLocation.ToLower()))
81  {
82  // Create the group
83  grec = new GroupData();
84  grec.GroupID = groupID;
85  grec.Data = new Dictionary<string, string>();
86  grec.Data["Name"] = name + " @ " + uri.Authority;
87  grec.Data["Location"] = serviceLocation;
88  grec.Data["Charter"] = string.Empty;
89  grec.Data["InsigniaID"] = UUID.Zero.ToString();
90  grec.Data["FounderID"] = UUID.Zero.ToString();
91  grec.Data["MembershipFee"] = "0";
92  grec.Data["OpenEnrollment"] = "0";
93  grec.Data["ShowInList"] = "0";
94  grec.Data["AllowPublish"] = "0";
95  grec.Data["MaturePublish"] = "0";
96  grec.Data["OwnerRoleID"] = UUID.Zero.ToString();
97 
98 
99  if (!m_Database.StoreGroup(grec))
100  return false;
101  }
102 
103  if (grec.Data["Location"] == string.Empty)
104  {
105  reason = "Cannot add proxy membership to non-proxy group";
106  return false;
107  }
108 
109  UUID uid = UUID.Zero;
110  string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
111  Util.ParseUniversalUserIdentifier(RequestingAgentID, out uid, out url, out first, out last, out tmp);
112  string fromName = first + "." + last + "@" + url;
113 
114  // Invite to group again
115  InviteToGroup(fromName, groupID, new UUID(agentID), grec.Data["Name"]);
116 
117  // Stick the proxy membership in the DB already
118  // we'll delete it if the agent declines the invitation
119  MembershipData membership = new MembershipData();
120  membership.PrincipalID = agentID;
121  membership.GroupID = groupID;
122  membership.Data = new Dictionary<string, string>();
123  membership.Data["SelectedRoleID"] = UUID.Zero.ToString();
124  membership.Data["Contribution"] = "0";
125  membership.Data["ListInProfile"] = "1";
126  membership.Data["AcceptNotices"] = "1";
127  membership.Data["AccessToken"] = accessToken;
128 
129  m_Database.StoreMember(membership);
130 
131  return true;
132  }
133 
134  public bool RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, string token)
135  {
136  // check the token
137  MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID);
138  if (membership != null)
139  {
140  if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
141  {
142  return RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
143  }
144  else
145  {
146  m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
147  return false;
148  }
149  }
150  else
151  {
152  m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", AgentID);
153  return false;
154  }
155  }
156 
157  public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token)
158  {
159  // check the token
160  if (!VerifyToken(GroupID, RequestingAgentID, token))
161  return null;
162 
163  ExtendedGroupRecord grec;
164  if (GroupID == UUID.Zero)
165  grec = GetGroupRecord(RequestingAgentID, groupName);
166  else
167  grec = GetGroupRecord(RequestingAgentID, GroupID);
168 
169  if (grec != null)
170  FillFounderUUI(grec);
171 
172  return grec;
173  }
174 
175  public List<ExtendedGroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
176  {
177  if (!VerifyToken(GroupID, RequestingAgentID, token))
178  return new List<ExtendedGroupMembersData>();
179 
180  List<ExtendedGroupMembersData> members = GetGroupMembers(RequestingAgentID, GroupID);
181 
182  // convert UUIDs to UUIs
183  members.ForEach(delegate (ExtendedGroupMembersData m)
184  {
185  if (m.AgentID.ToString().Length == 36) // UUID
186  {
187  UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.AgentID));
188  if (account != null)
189  m.AgentID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
190  }
191  });
192 
193  return members;
194  }
195 
196  public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
197  {
198  if (!VerifyToken(GroupID, RequestingAgentID, token))
199  return new List<GroupRolesData>();
200 
201  return GetGroupRoles(RequestingAgentID, GroupID);
202  }
203 
204  public List<ExtendedGroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
205  {
206  if (!VerifyToken(GroupID, RequestingAgentID, token))
207  return new List<ExtendedGroupRoleMembersData>();
208 
209  List<ExtendedGroupRoleMembersData> rolemembers = GetGroupRoleMembers(RequestingAgentID, GroupID);
210 
211  // convert UUIDs to UUIs
212  rolemembers.ForEach(delegate(ExtendedGroupRoleMembersData m)
213  {
214  if (m.MemberID.ToString().Length == 36) // UUID
215  {
216  UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.MemberID));
217  if (account != null)
218  m.MemberID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
219  }
220  });
221 
222  return rolemembers;
223  }
224 
225  public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
226  bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
227  {
228  // check that the group proxy exists
229  ExtendedGroupRecord grec = GetGroupRecord(RequestingAgentID, groupID);
230  if (grec == null)
231  {
232  m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to non-existent group proxy");
233  return false;
234  }
235 
236  // check that the group is remote
237  if (grec.ServiceLocation == string.Empty)
238  {
239  m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to local (non-proxy) group");
240  return false;
241  }
242 
243  // check that there isn't already a notice with the same ID
244  if (GetGroupNotice(RequestingAgentID, noticeID) != null)
245  {
246  m_log.DebugFormat("[Groups.HGGroupsService]: a notice with the same ID already exists", grec.ServiceLocation);
247  return false;
248  }
249 
250  // This has good intentions (security) but it will potentially DDS the origin...
251  // We'll need to send a proof along with the message. Maybe encrypt the message
252  // using key pairs
253  //
255  //GroupsServiceHGConnector c = new GroupsServiceHGConnector(grec.ServiceLocation);
256  //if (!c.VerifyNotice(noticeID, groupID))
257  //{
258  // m_log.DebugFormat("[Groups.HGGroupsService]: notice does not exist at origin {0}", grec.ServiceLocation);
259  // return false;
260  //}
261 
262  // ok, we're good!
263  return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID);
264  }
265 
266  public bool VerifyNotice(UUID noticeID, UUID groupID)
267  {
268  GroupNoticeInfo notice = GetGroupNotice(string.Empty, noticeID);
269 
270  if (notice == null)
271  return false;
272 
273  if (notice.GroupID != groupID)
274  return false;
275 
276  return true;
277  }
278 
279  #endregion
280 
281  private void InviteToGroup(string fromName, UUID groupID, UUID invitedAgentID, string groupName)
282  {
283  // Todo: Security check, probably also want to send some kind of notification
284  UUID InviteID = UUID.Random();
285 
286  if (AddAgentToGroupInvite(InviteID, groupID, invitedAgentID.ToString()))
287  {
288  Guid inviteUUID = InviteID.Guid;
289 
291 
292  msg.imSessionID = inviteUUID;
293 
294  // msg.fromAgentID = agentID.Guid;
295  msg.fromAgentID = groupID.Guid;
296  msg.toAgentID = invitedAgentID.Guid;
297  //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
298  msg.timestamp = 0;
299  msg.fromAgentName = fromName;
300  msg.message = string.Format("Please confirm your acceptance to join group {0}.", groupName);
301  msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
302  msg.fromGroup = true;
303  msg.offline = (byte)0;
304  msg.ParentEstateID = 0;
305  msg.Position = Vector3.Zero;
306  msg.RegionID = UUID.Zero.Guid;
307  msg.binaryBucket = new byte[20];
308 
309  string reason = string.Empty;
310  m_OfflineIM.StoreMessage(msg, out reason);
311 
312  }
313  }
314 
315  private bool AddAgentToGroupInvite(UUID inviteID, UUID groupID, string agentID)
316  {
317  // Check whether the invitee is already a member of the group
318  MembershipData m = m_Database.RetrieveMember(groupID, agentID);
319  if (m != null)
320  return false;
321 
322  // Check whether there are pending invitations and delete them
323  InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID);
324  if (invite != null)
325  m_Database.DeleteInvite(invite.InviteID);
326 
327  invite = new InvitationData();
328  invite.InviteID = inviteID;
329  invite.PrincipalID = agentID;
330  invite.GroupID = groupID;
331  invite.RoleID = UUID.Zero;
332  invite.Data = new Dictionary<string, string>();
333 
334  return m_Database.StoreInvitation(invite);
335  }
336 
337  private void FillFounderUUI(ExtendedGroupRecord grec)
338  {
339  UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, grec.FounderID);
340  if (account != null)
341  grec.FounderUUI = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
342  }
343 
344  private bool VerifyToken(UUID groupID, string agentID, string token)
345  {
346  // check the token
347  MembershipData membership = m_Database.RetrieveMember(groupID, agentID);
348  if (membership != null)
349  {
350  if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
351  return true;
352  else
353  m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
354  }
355  else
356  m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", agentID);
357 
358  return false;
359  }
360  }
361 }
HGGroupsService(IConfigSource config, IOfflineIMService im, IUserAccountService users, string homeURI)
bool VerifyNotice(UUID noticeID, UUID groupID)
List< GroupRolesData > GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
List< ExtendedGroupMembersData > GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
List< ExtendedGroupRoleMembersData > GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
Dictionary< string, string > Data
Definition: IGroupsData.cs:37
bool RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, string token)
ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token)
bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
bool CreateGroupProxy(string RequestingAgentID, string agentID, string accessToken, UUID groupID, string serviceLocation, string name, out string reason)
Dictionary< string, string > Data
Definition: IGroupsData.cs:44