opensim – Blame information for rev 1

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