clockwerk-opensim – Blame information for rev 1

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