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