OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
GroupsModule.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 Mono.Addins;
34 using Nini.Config;
35 using OpenMetaverse;
36 using OpenMetaverse.StructuredData;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 using OpenSim.Services.Interfaces;
41 using System.Text;
43 
44 namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
45 {
46  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsModule")]
48  {
73 
74  private static readonly ILog m_log =
75  LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
76 
77  private List<Scene> m_sceneList = new List<Scene>();
78 
79  private IMessageTransferModule m_msgTransferModule;
80 
81  private IGroupsMessagingModule m_groupsMessagingModule;
82 
83  private IGroupsServicesConnector m_groupData;
84 
85  // Configuration settings
86  private bool m_groupsEnabled = false;
87  private bool m_groupNoticesEnabled = true;
88  private bool m_debugEnabled = false;
89  private int m_levelGroupCreate = 0;
90 
91  #region Region Module interfaceBase Members
92 
93  public void Initialise(IConfigSource config)
94  {
95  IConfig groupsConfig = config.Configs["Groups"];
96 
97  if (groupsConfig == null)
98  {
99  // Do not run this module by default.
100  return;
101  }
102  else
103  {
104  m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false);
105  if (!m_groupsEnabled)
106  {
107  return;
108  }
109 
110  if (groupsConfig.GetString("Module", "Default") != Name)
111  {
112  m_groupsEnabled = false;
113 
114  return;
115  }
116 
117  m_log.InfoFormat("[GROUPS]: Initializing {0}", this.Name);
118 
119  m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true);
120  m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false);
121  m_levelGroupCreate = groupsConfig.GetInt("LevelGroupCreate", 0);
122  }
123  }
124 
125  public void AddRegion(Scene scene)
126  {
127  if (m_groupsEnabled)
128  {
129  scene.RegisterModuleInterface<IGroupsModule>(this);
130  scene.AddCommand(
131  "Debug",
132  this,
133  "debug groups verbose",
134  "debug groups verbose <true|false>",
135  "This setting turns on very verbose groups debugging",
136  HandleDebugGroupsVerbose);
137  }
138  }
139 
140  private void HandleDebugGroupsVerbose(object modules, string[] args)
141  {
142  if (args.Length < 4)
143  {
144  MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
145  return;
146  }
147 
148  bool verbose = false;
149  if (!bool.TryParse(args[3], out verbose))
150  {
151  MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
152  return;
153  }
154 
155  m_debugEnabled = verbose;
156 
157  MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
158  }
159 
160  public void RegionLoaded(Scene scene)
161  {
162  if (!m_groupsEnabled)
163  return;
164 
165  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
166 
167  if (m_groupData == null)
168  {
169  m_groupData = scene.RequestModuleInterface<IGroupsServicesConnector>();
170 
171  // No Groups Service Connector, then nothing works...
172  if (m_groupData == null)
173  {
174  m_groupsEnabled = false;
175  m_log.Error("[GROUPS]: Could not get IGroupsServicesConnector");
176  Close();
177  return;
178  }
179  }
180 
181  if (m_msgTransferModule == null)
182  {
183  m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
184 
185  // No message transfer module, no notices, group invites, rejects, ejects, etc
186  if (m_msgTransferModule == null)
187  {
188  m_groupsEnabled = false;
189  m_log.Warn("[GROUPS]: Could not get IMessageTransferModule");
190  }
191  }
192 
193  if (m_groupsMessagingModule == null)
194  {
195  m_groupsMessagingModule = scene.RequestModuleInterface<IGroupsMessagingModule>();
196 
197  // No message transfer module, no notices, group invites, rejects, ejects, etc
198  if (m_groupsMessagingModule == null)
199  m_log.Warn("[GROUPS]: Could not get IGroupsMessagingModule");
200  }
201 
202  lock (m_sceneList)
203  {
204  m_sceneList.Add(scene);
205  }
206 
207  scene.EventManager.OnNewClient += OnNewClient;
208  scene.EventManager.OnMakeRootAgent += OnMakeRoot;
209  scene.EventManager.OnMakeChildAgent += OnMakeChild;
210  scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
211  scene.EventManager.OnClientClosed += OnClientClosed;
212 
213  }
214 
215  public void RemoveRegion(Scene scene)
216  {
217  if (!m_groupsEnabled)
218  return;
219 
220  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
221 
222  lock (m_sceneList)
223  {
224  m_sceneList.Remove(scene);
225  }
226  }
227 
228  public void Close()
229  {
230  if (!m_groupsEnabled)
231  return;
232 
233  if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down Groups module.");
234  }
235 
236  public Type ReplaceableInterface
237  {
238  get { return null; }
239  }
240 
241  public string Name
242  {
243  get { return "GroupsModule"; }
244  }
245 
246  #endregion
247 
248  #region ISharedRegionModule Members
249 
250  public void PostInitialise()
251  {
252  // NoOp
253  }
254 
255  #endregion
256 
257  #region EventHandlers
258 
259  private void OnMakeRoot(ScenePresence sp)
260  {
261  if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
262 
263  sp.ControllingClient.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest;
264  // Used for Notices and Group Invites/Accept/Reject
265  sp.ControllingClient.OnInstantMessage += OnInstantMessage;
266 
267  // comented out because some viewers no longer suport it
268  // sp.ControllingClient.AddGenericPacketHandler("avatargroupsrequest", AvatarGroupsRequest);
269 
270  // we should send a DataUpdate here for compatibility,
271  // but this is a bad place and a bad thread to do it
272  // also current viewers do ignore it and ask later on a much nicer thread
273  }
274 
275  private void OnMakeChild(ScenePresence sp)
276  {
277  if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
278 
279  sp.ControllingClient.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
280  sp.ControllingClient.OnInstantMessage -= OnInstantMessage;
281  }
282 
283  private void OnNewClient(IClientAPI client)
284  {
285  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
286 
287  client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest;
288  client.OnRequestAvatarProperties += OnRequestAvatarProperties;
289 
290 
291  }
292 
293 /* this should be the right message to ask for other avatars groups
294 
295  private void AvatarGroupsRequest(Object sender, string method, List<String> args)
296  {
297  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
298 
299  if (!(sender is IClientAPI))
300  return;
301 
302  IClientAPI remoteClient = (IClientAPI)sender;
303 
304  UUID avatarID;
305  UUID.TryParse(args[0], out avatarID);
306 
307  if (avatarID != UUID.Zero)
308  {
309  GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID);
310  remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups);
311  }
312  }
313 */
314 
315  // this should not be used to send groups memberships, but some viewers do expect it
316  // it does send unnecessary memberships, when viewers just want other properties information
317  private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID)
318  {
319  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
320 
321  GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID);
322  remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups);
323  }
324 
325 
326  private void OnClientClosed(UUID AgentId, Scene scene)
327  {
328  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
329  if (scene == null)
330  return;
331 
332  ScenePresence sp = scene.GetScenePresence(AgentId);
333  IClientAPI client = sp.ControllingClient;
334  if (client != null)
335  {
336  client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest;
337  client.OnRequestAvatarProperties -= OnRequestAvatarProperties;
338  // make child possible not called?
339  client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
340  client.OnInstantMessage -= OnInstantMessage;
341  }
342 
343 /*
344  lock (m_ActiveClients)
345  {
346  if (m_ActiveClients.ContainsKey(AgentId))
347  {
348  IClientAPI client = m_ActiveClients[AgentId];
349  client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
350  client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest;
351  client.OnDirFindQuery -= OnDirFindQuery;
352  client.OnInstantMessage -= OnInstantMessage;
353 
354  m_ActiveClients.Remove(AgentId);
355  }
356  else
357  {
358  if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Client closed that wasn't registered here.");
359  }
360  }
361 */
362  }
363 
364  private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
365  {
366  // this a private message for own agent only
367  if (dataForAgentID != GetRequestingAgentID(remoteClient))
368  return;
369 
370  SendAgentGroupDataUpdate(remoteClient, false);
371  // its a info request not a change, so nothing is sent to others
372  // they do get the group title with the avatar object update on arrivel to a region
373  }
374 
375  private void HandleUUIDGroupNameRequest(UUID GroupID, IClientAPI remoteClient)
376  {
377  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
378 
379  string GroupName;
380 
381  GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null);
382  if (group != null)
383  {
384  GroupName = group.GroupName;
385  }
386  else
387  {
388  GroupName = "Unknown";
389  }
390 
391  remoteClient.SendGroupNameReply(GroupID, GroupName);
392  }
393 
394  private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
395  {
396  if (m_debugEnabled)
397  m_log.DebugFormat(
398  "[GROUPS]: {0} called for {1}, message type {2}",
399  System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name, (InstantMessageDialog)im.dialog);
400 
401  // Group invitations
402  if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
403  {
404  UUID inviteID = new UUID(im.imSessionID);
405  GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID);
406 
407  if (inviteInfo == null)
408  {
409  if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Received an Invite IM for an invite that does not exist {0}.", inviteID);
410  return;
411  }
412 
413  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID);
414 
415  UUID fromAgentID = new UUID(im.fromAgentID);
416  if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID))
417  {
418  // Accept
419  if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept)
420  {
421  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received an accept invite notice.");
422 
423  // and the sessionid is the role
424  m_groupData.AddAgentToGroup(GetRequestingAgentID(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID);
425 
427  msg.imSessionID = UUID.Zero.Guid;
428  msg.fromAgentID = UUID.Zero.Guid;
429  msg.toAgentID = inviteInfo.AgentID.Guid;
430  msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
431  msg.fromAgentName = "Groups";
432  msg.message = string.Format("You have been added to the group.");
433  msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox;
434  msg.fromGroup = false;
435  msg.offline = (byte)0;
436  msg.ParentEstateID = 0;
437  msg.Position = Vector3.Zero;
438  msg.RegionID = UUID.Zero.Guid;
439  msg.binaryBucket = new byte[0];
440 
441  OutgoingInstantMessage(msg, inviteInfo.AgentID);
442 
443  IClientAPI client = GetActiveClient(inviteInfo.AgentID);
444  if (client != null)
445  SendDataUpdate(remoteClient, true);
446 
447  // TODO: If the inviter is still online, they need an agent dataupdate
448  // and maybe group membership updates for the invitee
449 
450  m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID);
451  }
452 
453  // Reject
454  if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)
455  {
456  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received a reject invite notice.");
457  m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID);
458  }
459  }
460  }
461 
462  // Group notices
463  if ((im.dialog == (byte)InstantMessageDialog.GroupNotice))
464  {
465  if (!m_groupNoticesEnabled)
466  {
467  return;
468  }
469 
470  UUID GroupID = new UUID(im.toAgentID);
471  if (m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null) != null)
472  {
473  UUID NoticeID = UUID.Random();
474  string Subject = im.message.Substring(0, im.message.IndexOf('|'));
475  string Message = im.message.Substring(Subject.Length + 1);
476 
477  InventoryItemBase item = null;
478  bool hasAttachment = false;
479  UUID itemID = UUID.Zero; //Assignment to quiet compiler
480  UUID ownerID = UUID.Zero; //Assignment to quiet compiler
481  byte[] bucket;
482 
483  if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0)
484  {
485  string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
486  binBucket = binBucket.Remove(0, 14).Trim();
487 
488  OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket);
489  if (binBucketOSD is OSD)
490  {
491  OSDMap binBucketMap = (OSDMap)binBucketOSD;
492 
493  itemID = binBucketMap["item_id"].AsUUID();
494  ownerID = binBucketMap["owner_id"].AsUUID();
495 
496  //Attempt to get the details of the attached item.
497  //If sender doesn't own the attachment, the item
498  //variable will be set to null and attachment will
499  //not be included with the group notice.
500  Scene scene = (Scene)remoteClient.Scene;
501  item = new InventoryItemBase(itemID, ownerID);
502  item = scene.InventoryService.GetItem(item);
503 
504  if (item != null)
505  {
506  //Got item details so include the attachment.
507  hasAttachment = true;
508  }
509  }
510  else
511  {
512  m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType());
513  }
514  }
515 
516  if (hasAttachment)
517  {
518  //Bucket contains information about attachment.
519  //
520  //Byte offset and description of bucket data:
521  //0: 1 byte indicating if attachment is present
522  //1: 1 byte indicating the type of attachment
523  //2: 16 bytes - Group UUID
524  //18: 16 bytes - UUID of the attachment owner
525  //34: 16 bytes - UUID of the attachment
526  //50: variable - Name of the attachment
527  //??: NUL byte to terminate the attachment name
528  byte[] name = Encoding.UTF8.GetBytes(item.Name);
529  bucket = new byte[51 + name.Length];//3 bytes, 3 UUIDs, and name
530  bucket[0] = 1; //Has attachment flag
531  bucket[1] = (byte)item.InvType; //Type of Attachment
532  GroupID.ToBytes(bucket, 2);
533  ownerID.ToBytes(bucket, 18);
534  itemID.ToBytes(bucket, 34);
535  name.CopyTo(bucket, 50);
536  }
537  else
538  {
539  bucket = new byte[19];
540  bucket[0] = 0; //Has attachment flag
541  bucket[1] = 0; //Type of attachment
542  GroupID.ToBytes(bucket, 2);
543  bucket[18] = 0; //NUL terminate name of attachment
544  }
545 
546  m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket);
547  if (OnNewGroupNotice != null)
548  {
549  OnNewGroupNotice(GroupID, NoticeID);
550  }
551 
552  if (m_debugEnabled)
553  {
554  foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID))
555  {
556  if (m_debugEnabled)
557  {
558  UserAccount targetUser
559  = m_sceneList[0].UserAccountService.GetUserAccount(
560  remoteClient.Scene.RegionInfo.ScopeID, member.AgentID);
561 
562  if (targetUser != null)
563  {
564  m_log.DebugFormat(
565  "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
566  NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices);
567  }
568  else
569  {
570  m_log.DebugFormat(
571  "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
572  NoticeID, member.AgentID, member.AcceptNotices);
573  }
574  }
575  }
576  }
577 
578  GridInstantMessage msg
579  = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
580 
581  if (m_groupsMessagingModule != null)
582  m_groupsMessagingModule.SendMessageToGroup(
583  msg, GroupID, remoteClient.AgentId, gmd => gmd.AcceptNotices);
584  }
585  }
586 
587  if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted)
588  {
589  //Is bucket large enough to hold UUID of the attachment?
590  if (im.binaryBucket.Length < 16)
591  return;
592 
593  UUID noticeID = new UUID(im.imSessionID);
594 
595  if (m_debugEnabled)
596  m_log.DebugFormat("[GROUPS]: Requesting notice {0} for {1}", noticeID, remoteClient.AgentId);
597 
598  GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID);
599  if (notice != null)
600  {
601  UUID giver = new UUID(notice.BinaryBucket, 18);
602  UUID attachmentUUID = new UUID(notice.BinaryBucket, 34);
603 
604  if (m_debugEnabled)
605  m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId);
606 
607  string message;
608  InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId,
609  giver, attachmentUUID, out message);
610 
611  if (itemCopy == null)
612  {
613  remoteClient.SendAgentAlertMessage(message, false);
614  return;
615  }
616 
617  remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0);
618  }
619  else
620  {
621  if (m_debugEnabled)
622  m_log.DebugFormat(
623  "[GROUPS]: Could not find notice {0} for {1} on GroupNoticeInventoryAccepted.",
624  noticeID, remoteClient.AgentId);
625  }
626  }
627 
628  // Interop, received special 210 code for ejecting a group member
629  // this only works within the comms servers domain, and won't work hypergrid
630  // TODO:FIXME: Use a presence server of some kind to find out where the
631  // client actually is, and try contacting that region directly to notify them,
632  // or provide the notification via xmlrpc update queue
633  if ((im.dialog == 210))
634  {
635  // This is sent from the region that the ejectee was ejected from
636  // if it's being delivered here, then the ejectee is here
637  // so we need to send local updates to the agent.
638 
639  UUID ejecteeID = new UUID(im.toAgentID);
640 
641  im.dialog = (byte)InstantMessageDialog.MessageFromAgent;
642  OutgoingInstantMessage(im, ejecteeID);
643 
644  IClientAPI ejectee = GetActiveClient(ejecteeID);
645  if (ejectee != null)
646  {
647  UUID groupID = new UUID(im.imSessionID);
648  ejectee.SendAgentDropGroup(groupID);
649  }
650  }
651  }
652 
653  private void OnGridInstantMessage(GridInstantMessage msg)
654  {
655  if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
656 
657  // Trigger the above event handler
658  OnInstantMessage(null, msg);
659 
660  // If a message from a group arrives here, it may need to be forwarded to a local client
661  if (msg.fromGroup == true)
662  {
663  switch (msg.dialog)
664  {
665  case (byte)InstantMessageDialog.GroupInvitation:
666  case (byte)InstantMessageDialog.GroupNotice:
667  UUID toAgentID = new UUID(msg.toAgentID);
668  IClientAPI localClient = GetActiveClient(toAgentID);
669  if (localClient != null)
670  {
671  localClient.SendInstantMessage(msg);
672  }
673  break;
674  }
675  }
676  }
677 
678  #endregion
679 
680  #region IGroupsModule Members
681 
683 
684  public GroupRecord GetGroupRecord(UUID GroupID)
685  {
686  return m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
687  }
688 
689  public GroupRecord GetGroupRecord(string name)
690  {
691  return m_groupData.GetGroupRecord(UUID.Zero, UUID.Zero, name);
692  }
693 
694  public void ActivateGroup(IClientAPI remoteClient, UUID groupID)
695  {
696  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
697 
698  UUID agentID = GetRequestingAgentID(remoteClient);
699  m_groupData.SetAgentActiveGroup(agentID, agentID, groupID);
700 
701  // llClientView does this
702  SendAgentGroupDataUpdate(remoteClient, true);
703  }
704 
708  public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
709  {
710  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
711 
712  List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
713  GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
714 
715  List<GroupTitlesData> titles = new List<GroupTitlesData>();
716  foreach (GroupRolesData role in agentRoles)
717  {
718  GroupTitlesData title = new GroupTitlesData();
719  title.Name = role.Name;
720  if (agentMembership != null)
721  {
722  title.Selected = agentMembership.ActiveRole == role.RoleID;
723  }
724  title.UUID = role.RoleID;
725 
726  titles.Add(title);
727  }
728 
729  return titles;
730  }
731 
732  public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
733  {
734  if (m_debugEnabled)
735  m_log.DebugFormat(
736  "[GROUPS]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name);
737 
738  List<GroupMembersData> data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID);
739 
740  if (m_debugEnabled)
741  {
742  foreach (GroupMembersData member in data)
743  {
744  m_log.DebugFormat("[GROUPS]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner);
745  }
746  }
747 
748  return data;
749  }
750 
751  public List<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
752  {
753  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
754 
755  List<GroupRolesData> data = m_groupData.GetGroupRoles(GetRequestingAgentID(remoteClient), groupID);
756 
757  return data;
758  }
759 
760  public List<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
761  {
762  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
763 
764  List<GroupRoleMembersData> data = m_groupData.GetGroupRoleMembers(GetRequestingAgentID(remoteClient), groupID);
765 
766  if (m_debugEnabled)
767  {
768  foreach (GroupRoleMembersData member in data)
769  {
770  m_log.DebugFormat("[GROUPS]: Member({0}) - Role({1})", member.MemberID, member.RoleID);
771  }
772  }
773  return data;
774  }
775 
776  public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID)
777  {
778  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
779 
780  GroupProfileData profile = new GroupProfileData();
781 
782 
783  GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), groupID, null);
784  if (groupInfo != null)
785  {
786  profile.AllowPublish = groupInfo.AllowPublish;
787  profile.Charter = groupInfo.Charter;
788  profile.FounderID = groupInfo.FounderID;
789  profile.GroupID = groupID;
790  profile.GroupMembershipCount = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID).Count;
791  profile.GroupRolesCount = m_groupData.GetGroupRoles(GetRequestingAgentID(remoteClient), groupID).Count;
792  profile.InsigniaID = groupInfo.GroupPicture;
793  profile.MaturePublish = groupInfo.MaturePublish;
794  profile.MembershipFee = groupInfo.MembershipFee;
795  profile.Money = 0; // TODO: Get this from the currency server?
796  profile.Name = groupInfo.GroupName;
797  profile.OpenEnrollment = groupInfo.OpenEnrollment;
798  profile.OwnerRole = groupInfo.OwnerRoleID;
799  profile.ShowInList = groupInfo.ShowInList;
800  }
801 
802  GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
803  if (memberInfo != null)
804  {
805  profile.MemberTitle = memberInfo.GroupTitle;
806  profile.PowersMask = memberInfo.GroupPowers;
807  }
808 
809  return profile;
810  }
811 
812  public GroupMembershipData[] GetMembershipData(UUID agentID)
813  {
814  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
815 
816  return m_groupData.GetAgentGroupMemberships(UUID.Zero, agentID).ToArray();
817  }
818 
819  public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
820  {
821  if (m_debugEnabled)
822  m_log.DebugFormat(
823  "[GROUPS]: {0} called with groupID={1}, agentID={2}",
824  System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID);
825 
826  return m_groupData.GetAgentGroupMembership(UUID.Zero, agentID, groupID);
827  }
828 
829  public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
830  {
831  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
832 
833  // Note: Permissions checking for modification rights is handled by the Groups Server/Service
834  m_groupData.UpdateGroup(GetRequestingAgentID(remoteClient), groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
835  }
836 
837  public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile)
838  {
839  // Note: Permissions checking for modification rights is handled by the Groups Server/Service
840  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
841 
842  m_groupData.SetAgentGroupInfo(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID, acceptNotices, listInProfile);
843  }
844 
845  public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
846  {
847  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
848 
849  if (m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), UUID.Zero, name) != null)
850  {
851  remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists.");
852  return UUID.Zero;
853  }
854 
855  // check user level
856  ScenePresence avatar = null;
857  Scene scene = (Scene)remoteClient.Scene;
858  scene.TryGetScenePresence(remoteClient.AgentId, out avatar);
859 
860  if (avatar != null)
861  {
862  if (avatar.UserLevel < m_levelGroupCreate)
863  {
864  remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got insufficient permissions to create a group.");
865  return UUID.Zero;
866  }
867  }
868 
869  // check funds
870  // is there is a money module present ?
871  IMoneyModule money = scene.RequestModuleInterface<IMoneyModule>();
872  if (money != null)
873  {
874  // do the transaction, that is if the agent has got sufficient funds
875  if (!money.AmountCovered(remoteClient.AgentId, money.GroupCreationCharge)) {
876  remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got insufficient funds to create a group.");
877  return UUID.Zero;
878  }
879  money.ApplyCharge(GetRequestingAgentID(remoteClient), money.GroupCreationCharge, MoneyTransactionType.GroupCreate);
880  }
881  UUID groupID = m_groupData.CreateGroup(GetRequestingAgentID(remoteClient), name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, GetRequestingAgentID(remoteClient));
882 
883  remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly");
884 
885  // Update the founder with new group information.
886  SendAgentGroupDataUpdate(remoteClient, false);
887 
888  return groupID;
889  }
890 
891  public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID)
892  {
893  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
894 
895  // ToDo: check if agent is a member of group and is allowed to see notices?
896 
897  return m_groupData.GetGroupNotices(GetRequestingAgentID(remoteClient), groupID).ToArray();
898  }
899 
903  public string GetGroupTitle(UUID avatarID)
904  {
905  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
906 
907  GroupMembershipData membership = m_groupData.GetAgentActiveMembership(UUID.Zero, avatarID);
908  if (membership != null)
909  {
910  return membership.GroupTitle;
911  }
912  return string.Empty;
913  }
914 
918  public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID)
919  {
920  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
921 
922  m_groupData.SetAgentActiveGroupRole(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID, titleRoleID);
923 
924  // TODO: Not sure what all is needed here, but if the active group role change is for the group
925  // the client currently has set active, then we need to do a scene presence update too
926  // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID)
927 
928  SendDataUpdate(remoteClient, true);
929  }
930 
931  public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType)
932  {
933  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
934 
935  // Security Checks are handled in the Groups Service.
936 
937  switch ((OpenMetaverse.GroupRoleUpdate)updateType)
938  {
939  case OpenMetaverse.GroupRoleUpdate.Create:
940  m_groupData.AddGroupRole(GetRequestingAgentID(remoteClient), groupID, UUID.Random(), name, description, title, powers);
941  break;
942 
943  case OpenMetaverse.GroupRoleUpdate.Delete:
944  m_groupData.RemoveGroupRole(GetRequestingAgentID(remoteClient), groupID, roleID);
945  break;
946 
947  case OpenMetaverse.GroupRoleUpdate.UpdateAll:
948  case OpenMetaverse.GroupRoleUpdate.UpdateData:
949  case OpenMetaverse.GroupRoleUpdate.UpdatePowers:
950  if (m_debugEnabled)
951  {
952  GroupPowers gp = (GroupPowers)powers;
953  m_log.DebugFormat("[GROUPS]: Role ({0}) updated with Powers ({1}) ({2})", name, powers.ToString(), gp.ToString());
954  }
955  m_groupData.UpdateGroupRole(GetRequestingAgentID(remoteClient), groupID, roleID, name, description, title, powers);
956  break;
957 
958  case OpenMetaverse.GroupRoleUpdate.NoUpdate:
959  default:
960  // No Op
961  break;
962 
963  }
964 
965  // TODO: This update really should send out updates for everyone in the role that just got changed.
966  SendAgentGroupDataUpdate(remoteClient, false);
967  }
968 
969  public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes)
970  {
971  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
972  // Todo: Security check
973 
974  switch (changes)
975  {
976  case 0:
977  // Add
978  m_groupData.AddAgentToGroupRole(GetRequestingAgentID(remoteClient), memberID, groupID, roleID);
979 
980  break;
981  case 1:
982  // Remove
983  m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentID(remoteClient), memberID, groupID, roleID);
984 
985  break;
986  default:
987  m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes);
988  break;
989  }
990 
991  // TODO: This update really should send out updates for everyone in the role that just got changed.
992  SendAgentGroupDataUpdate(remoteClient, false);
993  }
994 
995  public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID)
996  {
997  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
998 
999  GroupNoticeInfo data = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), groupNoticeID);
1000 
1001  if (data != null)
1002  {
1003  GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested);
1004 
1005  OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient));
1006  }
1007  }
1008 
1009  public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
1010  {
1011  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1012 
1014  byte[] bucket;
1015 
1016  msg.imSessionID = groupNoticeID.Guid;
1017  msg.toAgentID = agentID.Guid;
1018  msg.dialog = dialog;
1019  msg.fromGroup = true;
1020  msg.offline = (byte)1; // Allow this message to be stored for offline use
1021  msg.ParentEstateID = 0;
1022  msg.Position = Vector3.Zero;
1023  msg.RegionID = UUID.Zero.Guid;
1024 
1025  GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID, groupNoticeID);
1026  if (info != null)
1027  {
1028  msg.fromAgentID = info.GroupID.Guid;
1029  msg.timestamp = info.noticeData.Timestamp;
1030  msg.fromAgentName = info.noticeData.FromName;
1031  msg.message = info.noticeData.Subject + "|" + info.Message;
1032 
1033  if (info.BinaryBucket[0] > 0)
1034  {
1035  //32 is due to not needing space for two of the UUIDs.
1036  //(Don't need UUID of attachment or its owner in IM)
1037  //50 offset gets us to start of attachment name.
1038  //We are skipping the attachment flag, type, and
1039  //the three UUID fields at the start of the bucket.
1040  bucket = new byte[info.BinaryBucket.Length-32];
1041  bucket[0] = 1; //Has attachment
1042  bucket[1] = info.BinaryBucket[1];
1043  Array.Copy(info.BinaryBucket, 50,
1044  bucket, 18, info.BinaryBucket.Length-50);
1045  }
1046  else
1047  {
1048  bucket = new byte[19];
1049  bucket[0] = 0; //No attachment
1050  bucket[1] = 0; //Attachment type
1051  bucket[18] = 0; //NUL terminate name
1052  }
1053 
1054  info.GroupID.ToBytes(bucket, 2);
1055  msg.binaryBucket = bucket;
1056  }
1057  else
1058  {
1059  if (m_debugEnabled)
1060  m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID);
1061 
1062  msg.fromAgentID = UUID.Zero.Guid;
1063  msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1064  msg.fromAgentName = string.Empty;
1065  msg.message = string.Empty;
1066  msg.binaryBucket = new byte[0];
1067  }
1068 
1069  return msg;
1070  }
1071 
1072  public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID)
1073  {
1074  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1075 
1076  // Should check to see if OpenEnrollment, or if there's an outstanding invitation
1077  m_groupData.AddAgentToGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID, UUID.Zero);
1078 
1079  remoteClient.SendJoinGroupReply(groupID, true);
1080 
1081  SendAgentGroupDataUpdate(remoteClient, true);
1082  }
1083 
1084  public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID)
1085  {
1086  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1087 
1088  m_groupData.RemoveAgentFromGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
1089 
1090  remoteClient.SendLeaveGroupReply(groupID, true);
1091 
1092  remoteClient.SendAgentDropGroup(groupID);
1093 
1094  // SL sends out notifcations to the group messaging session that the person has left
1095  // Should this also update everyone who is in the group?
1096  SendAgentGroupDataUpdate(remoteClient, true);
1097  }
1098 
1099  public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID)
1100  {
1101  EjectGroupMember(remoteClient, GetRequestingAgentID(remoteClient), groupID, ejecteeID);
1102  }
1103 
1104  public void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID)
1105  {
1106  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1107 
1108  // Todo: Security check?
1109  m_groupData.RemoveAgentFromGroup(agentID, ejecteeID, groupID);
1110 
1111  string agentName;
1112  RegionInfo regionInfo;
1113 
1114  // remoteClient provided or just agentID?
1115  if (remoteClient != null)
1116  {
1117  agentName = remoteClient.Name;
1118  regionInfo = remoteClient.Scene.RegionInfo;
1119  remoteClient.SendEjectGroupMemberReply(agentID, groupID, true);
1120  }
1121  else
1122  {
1123  IClientAPI client = GetActiveClient(agentID);
1124 
1125  if (client != null)
1126  {
1127  agentName = client.Name;
1128  regionInfo = client.Scene.RegionInfo;
1129  client.SendEjectGroupMemberReply(agentID, groupID, true);
1130  }
1131  else
1132  {
1133  regionInfo = m_sceneList[0].RegionInfo;
1134  UserAccount acc = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID);
1135 
1136  if (acc != null)
1137  {
1138  agentName = acc.FirstName + " " + acc.LastName;
1139  }
1140  else
1141  {
1142  agentName = "Unknown member";
1143  }
1144  }
1145  }
1146 
1147  GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID, groupID, null);
1148 
1149  UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, ejecteeID);
1150  if ((groupInfo == null) || (account == null))
1151  {
1152  return;
1153  }
1154 
1155  // Send Message to Ejectee
1157 
1158  msg.imSessionID = UUID.Zero.Guid;
1159  msg.fromAgentID = agentID.Guid;
1160  // msg.fromAgentID = info.GroupID;
1161  msg.toAgentID = ejecteeID.Guid;
1162  //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1163  msg.timestamp = 0;
1164  msg.fromAgentName = agentName;
1165  msg.message = string.Format("You have been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName);
1166  msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent;
1167  msg.fromGroup = false;
1168  msg.offline = (byte)0;
1169  msg.ParentEstateID = 0;
1170  msg.Position = Vector3.Zero;
1171  msg.RegionID = regionInfo.RegionID.Guid;
1172  msg.binaryBucket = new byte[0];
1173  OutgoingInstantMessage(msg, ejecteeID);
1174 
1175  // Message to ejector
1176  // Interop, received special 210 code for ejecting a group member
1177  // this only works within the comms servers domain, and won't work hypergrid
1178  // TODO:FIXME: Use a presence server of some kind to find out where the
1179  // client actually is, and try contacting that region directly to notify them,
1180  // or provide the notification via xmlrpc update queue
1181 
1182  msg = new GridInstantMessage();
1183  msg.imSessionID = UUID.Zero.Guid;
1184  msg.fromAgentID = agentID.Guid;
1185  msg.toAgentID = agentID.Guid;
1186  msg.timestamp = 0;
1187  msg.fromAgentName = agentName;
1188  if (account != null)
1189  {
1190  msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, account.FirstName + " " + account.LastName);
1191  }
1192  else
1193  {
1194  msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, "Unknown member");
1195  }
1196  msg.dialog = (byte)210; //interop
1197  msg.fromGroup = false;
1198  msg.offline = (byte)0;
1199  msg.ParentEstateID = 0;
1200  msg.Position = Vector3.Zero;
1201  msg.RegionID = regionInfo.RegionID.Guid;
1202  msg.binaryBucket = new byte[0];
1203  OutgoingInstantMessage(msg, agentID);
1204 
1205  // SL sends out messages to everyone in the group
1206  // Who all should receive updates and what should they be updated with?
1207  // just tell this the group change
1208  SendAgentGroupDataUpdate(remoteClient, false);
1209  // TODO fix the rest of sends
1210  }
1211 
1212  public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID)
1213  {
1214  InviteGroup(remoteClient, GetRequestingAgentID(remoteClient), groupID, invitedAgentID, roleID);
1215  }
1216 
1217  public void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID)
1218  {
1219  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1220 
1221  string agentName;
1222  RegionInfo regionInfo;
1223 
1224  // remoteClient provided or just agentID?
1225  if (remoteClient != null)
1226  {
1227  agentName = remoteClient.Name;
1228  regionInfo = remoteClient.Scene.RegionInfo;
1229  }
1230  else
1231  {
1232  IClientAPI client = GetActiveClient(agentID);
1233 
1234  if (client != null)
1235  {
1236  agentName = client.Name;
1237  regionInfo = client.Scene.RegionInfo;
1238  }
1239  else
1240  {
1241  regionInfo = m_sceneList[0].RegionInfo;
1242  UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID);
1243 
1244  if (account != null)
1245  {
1246  agentName = account.FirstName + " " + account.LastName;
1247  }
1248  else
1249  {
1250  agentName = "Unknown member";
1251  }
1252  }
1253  }
1254 
1255  // Todo: Security check, probably also want to send some kind of notification
1256  UUID InviteID = UUID.Random();
1257 
1258  m_groupData.AddAgentToGroupInvite(agentID, InviteID, groupID, roleID, invitedAgentID);
1259 
1260  // Check to see if the invite went through, if it did not then it's possible
1261  // the remoteClient did not validate or did not have permission to invite.
1262  GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(agentID, InviteID);
1263 
1264  if (inviteInfo != null)
1265  {
1266  if (m_msgTransferModule != null)
1267  {
1268  Guid inviteUUID = InviteID.Guid;
1269 
1271 
1272  msg.imSessionID = inviteUUID;
1273 
1274  // msg.fromAgentID = agentID.Guid;
1275  msg.fromAgentID = groupID.Guid;
1276  msg.toAgentID = invitedAgentID.Guid;
1277  //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1278  msg.timestamp = 0;
1279  msg.fromAgentName = agentName;
1280  msg.message = string.Format("{0} has invited you to join a group. There is no cost to join this group.", agentName);
1281  msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
1282  msg.fromGroup = true;
1283  msg.offline = (byte)0;
1284  msg.ParentEstateID = 0;
1285  msg.Position = Vector3.Zero;
1286  msg.RegionID = regionInfo.RegionID.Guid;
1287  msg.binaryBucket = new byte[20];
1288 
1289  OutgoingInstantMessage(msg, invitedAgentID);
1290  }
1291  }
1292  }
1293 
1294  public List<DirGroupsReplyData> FindGroups(IClientAPI remoteClient, string query)
1295  {
1296  return m_groupData.FindGroups(GetRequestingAgentID(remoteClient), query);
1297  }
1298 
1299 
1300  #endregion
1301 
1302  #region Client/Update Tools
1303 
1307  private IClientAPI GetActiveClient(UUID agentID)
1308  {
1309  IClientAPI child = null;
1310 
1311  // Try root avatar first
1312  foreach (Scene scene in m_sceneList)
1313  {
1314  ScenePresence sp = scene.GetScenePresence(agentID);
1315  if (sp != null)
1316  {
1317  if (!sp.IsChildAgent)
1318  {
1319  return sp.ControllingClient;
1320  }
1321  else
1322  {
1323  child = sp.ControllingClient;
1324  }
1325  }
1326  }
1327 
1328  // If we didn't find a root, then just return whichever child we found, or null if none
1329  return child;
1330  }
1331 
1332  private void SendScenePresenceUpdate(UUID AgentID, string Title)
1333  {
1334  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Updating scene title for {0} with title: {1}", AgentID, Title);
1335 
1336  ScenePresence presence = null;
1337 
1338  foreach (Scene scene in m_sceneList)
1339  {
1340  presence = scene.GetScenePresence(AgentID);
1341  if (presence != null)
1342  {
1343  if (presence.Grouptitle != Title)
1344  {
1345  presence.Grouptitle = Title;
1346 
1347  if (! presence.IsChildAgent)
1348  presence.SendAvatarDataToAllAgents();
1349  }
1350  }
1351  }
1352  }
1353 
1354  public void SendAgentGroupDataUpdate(IClientAPI remoteClient)
1355  {
1356  SendAgentGroupDataUpdate(remoteClient, true);
1357  }
1358 
1362  private void SendAgentGroupDataUpdate(IClientAPI remoteClient, bool tellOthers)
1363  {
1364  if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name);
1365 
1366  // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
1367 
1368  UUID agentID = GetRequestingAgentID(remoteClient);
1369 
1370  SendDataUpdate(remoteClient, tellOthers);
1371 
1372  GroupMembershipData[] membershipArray = GetProfileListedGroupMemberships(remoteClient, agentID);
1373  remoteClient.SendAgentGroupDataUpdate(agentID, membershipArray);
1374 
1375  remoteClient.RefreshGroupMembership();
1376  }
1377 
1384  private GroupMembershipData[] GetProfileListedGroupMemberships(IClientAPI requestingClient, UUID dataForAgentID)
1385  {
1386  List<GroupMembershipData> membershipData = m_groupData.GetAgentGroupMemberships(requestingClient.AgentId, dataForAgentID);
1387  GroupMembershipData[] membershipArray;
1388 
1389  // cScene and property accessor 'isGod' are in support of the opertions to bypass 'hidden' group attributes for
1390  // those with a GodLike aspect.
1391  Scene cScene = (Scene)requestingClient.Scene;
1392  bool isGod = cScene.Permissions.IsGod(requestingClient.AgentId);
1393 
1394  if (isGod)
1395  {
1396  membershipArray = membershipData.ToArray();
1397  }
1398  else
1399  {
1400  if (requestingClient.AgentId != dataForAgentID)
1401  {
1402  Predicate<GroupMembershipData> showInProfile = delegate(GroupMembershipData membership)
1403  {
1404  return membership.ListInProfile;
1405  };
1406 
1407  membershipArray = membershipData.FindAll(showInProfile).ToArray();
1408  }
1409  else
1410  {
1411  membershipArray = membershipData.ToArray();
1412  }
1413  }
1414 
1415  if (m_debugEnabled)
1416  {
1417  m_log.InfoFormat("[GROUPS]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId);
1418  foreach (GroupMembershipData membership in membershipArray)
1419  {
1420  m_log.InfoFormat("[GROUPS]: {0} :: {1} - {2} - {3}", dataForAgentID, membership.GroupName, membership.GroupTitle, membership.GroupPowers);
1421  }
1422  }
1423 
1424  return membershipArray;
1425  }
1426 
1427  //tell remoteClient about its agent group info, and optionally send title to others
1428  private void SendDataUpdate(IClientAPI remoteClient, bool tellOthers)
1429  {
1430  if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1431 
1432  UUID activeGroupID = UUID.Zero;
1433  string activeGroupTitle = string.Empty;
1434  string activeGroupName = string.Empty;
1435  ulong activeGroupPowers = (ulong)GroupPowers.None;
1436 
1437  UUID agentID = GetRequestingAgentID(remoteClient);
1438  GroupMembershipData membership = m_groupData.GetAgentActiveMembership(agentID, agentID);
1439  if (membership != null)
1440  {
1441  activeGroupID = membership.GroupID;
1442  activeGroupTitle = membership.GroupTitle;
1443  activeGroupPowers = membership.GroupPowers;
1444  activeGroupName = membership.GroupName;
1445  }
1446 
1447  UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, agentID);
1448  string firstname, lastname;
1449  if (account != null)
1450  {
1451  firstname = account.FirstName;
1452  lastname = account.LastName;
1453  }
1454  else
1455  {
1456  firstname = "Unknown";
1457  lastname = "Unknown";
1458  }
1459 
1460  remoteClient.SendAgentDataUpdate(agentID, activeGroupID, firstname,
1461  lastname, activeGroupPowers, activeGroupName,
1462  activeGroupTitle);
1463 
1464  if (tellOthers)
1465  SendScenePresenceUpdate(agentID, activeGroupTitle);
1466 
1467  ScenePresence sp = (ScenePresence)remoteClient.SceneAgent;
1468  if (sp != null)
1469  sp.Grouptitle = activeGroupTitle;
1470  }
1471 
1472  #endregion
1473 
1474  #region IM Backed Processes
1475 
1476  private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo)
1477  {
1478  if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1479 
1480  IClientAPI localClient = GetActiveClient(msgTo);
1481  if (localClient != null)
1482  {
1483  if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is local, delivering directly", localClient.Name);
1484  localClient.SendInstantMessage(msg);
1485  }
1486  else if (m_msgTransferModule != null)
1487  {
1488  if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo);
1489  m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Message Sent: {0}", success?"Succeeded":"Failed"); });
1490  }
1491  }
1492 
1493  public void NotifyChange(UUID groupID)
1494  {
1495  // Notify all group members of a chnge in group roles and/or
1496  // permissions
1497  //
1498  }
1499 
1500  #endregion
1501 
1502  private UUID GetRequestingAgentID(IClientAPI client)
1503  {
1504  UUID requestingAgentID = UUID.Zero;
1505  if (client != null)
1506  {
1507  requestingAgentID = client.AgentId;
1508  }
1509  return requestingAgentID;
1510  }
1511  }
1512 
1513  public class GroupNoticeInfo
1514  {
1515  public GroupNoticeData noticeData = new GroupNoticeData();
1516  public UUID GroupID = UUID.Zero;
1517  public string Message = string.Empty;
1518  public byte[] BinaryBucket = new byte[0];
1519  }
1520 }
void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType)
void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID)
string GetGroupTitle(UUID avatarID)
Get the title of the agent's current role.
void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes)
GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID)
bool AmountCovered(UUID agentID, int amount)
GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID)
delegate void NewGroupNotice(UUID groupID, UUID noticeID)
OpenSim.Framework.RegionInfo RegionInfo
List< DirGroupsReplyData > FindGroups(IClientAPI remoteClient, string query)
string Name
Returns the full name of the agent/avatar represented by this client
Definition: IClientAPI.cs:754
OpenMetaverse.StructuredData.OSDMap OSDMap
GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
List< GroupRoleMembersData > GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID)
void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID)
List< GroupMembersData > GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
ISceneAgent SceneAgent
The scene agent for this client. This will only be set if the client has an agent in a scene (i...
Definition: IClientAPI.cs:724
UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
Create a group
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID)
void JoinGroupRequest(IClientAPI remoteClient, UUID groupID)
List< GroupTitlesData > GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
Get the Role Titles for an Agent, for a specific group
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
List< GroupRolesData > GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID)
Inventory Item - contains all the properties associated with an individual inventory piece...
void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID)
Change the current Active Group Role for Agent
void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID)
void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile)
OpenMetaverse.StructuredData.OSD OSD
GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
override bool TryGetScenePresence(UUID agentID, out ScenePresence sp)
Try to get a scene presence from the scene
Definition: Scene.cs:5401
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
Definition: GroupsModule.cs:93
void ActivateGroup(IClientAPI remoteClient, UUID groupID)
OpenMetaverse.DirectoryManager.DirFindFlags DirFindFlags
Definition: GroupsModule.cs:41