aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
diff options
context:
space:
mode:
authorSean Dague2009-08-07 11:07:41 -0400
committerSean Dague2009-08-07 11:07:41 -0400
commitca5da5face978860ee2071a63f735ebc9aa49aee (patch)
tree1829c93bceb58539e6d22d6bca213cdd5dc9b039 /OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
parentadd in stanzas for ppc64 and s390x (diff)
parentMerge branch 'master' into groups-core-contrib (diff)
downloadopensim-SC_OLD-ca5da5face978860ee2071a63f735ebc9aa49aee.zip
opensim-SC_OLD-ca5da5face978860ee2071a63f735ebc9aa49aee.tar.gz
opensim-SC_OLD-ca5da5face978860ee2071a63f735ebc9aa49aee.tar.bz2
opensim-SC_OLD-ca5da5face978860ee2071a63f735ebc9aa49aee.tar.xz
Merge branch 'groups-core-contrib' of git://github.com/mcortez/opensim into incoming
Diffstat (limited to 'OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs')
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
new file mode 100644
index 0000000..725c303
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
@@ -0,0 +1,1342 @@
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
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Timers;
32
33using log4net;
34using Mono.Addins;
35using Nini.Config;
36
37using OpenMetaverse;
38using OpenMetaverse.StructuredData;
39
40using OpenSim.Framework;
41using OpenSim.Framework.Communications;
42using OpenSim.Region.CoreModules.Framework.EventQueue;
43using OpenSim.Region.Framework.Interfaces;
44using OpenSim.Region.Framework.Scenes;
45
46using Caps = OpenSim.Framework.Capabilities.Caps;
47using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags;
48
49
50
51namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
52{
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
54 public class GroupsModule : ISharedRegionModule, IGroupsModule
55 {
56 /// <summary>
57 /// ; To use this module, you must specify the following in your OpenSim.ini
58 /// [GROUPS]
59 /// Enabled = true
60 ///
61 /// Module = GroupsModule
62 /// NoticesEnabled = true
63 /// DebugEnabled = true
64 ///
65 /// GroupsServicesConnectorModule = XmlRpcGroupsServicesConnector
66 /// XmlRpcServiceURL = http://osflotsam.org/xmlrpc.php
67 /// XmlRpcServiceReadKey = 1234
68 /// XmlRpcServiceWriteKey = 1234
69 ///
70 /// MessagingModule = GroupsMessagingModule
71 /// MessagingEnabled = true
72 ///
73 /// ; Disables HTTP Keep-Alive for Groups Module HTTP Requests, work around for
74 /// ; a problem discovered on some Windows based region servers. Only disable
75 /// ; if you see a large number (dozens) of the following Exceptions:
76 /// ; System.Net.WebException: The request was aborted: The request was canceled.
77 ///
78 /// XmlRpcDisableKeepAlive = false
79 /// </summary>
80
81 private static readonly ILog m_log =
82 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
83
84 private List<Scene> m_sceneList = new List<Scene>();
85
86 private IMessageTransferModule m_msgTransferModule = null;
87
88 private IGroupsServicesConnector m_groupData = null;
89
90 class GroupRequestIDInfo
91 {
92 public GroupRequestID RequestID = new GroupRequestID();
93 public DateTime LastUsedTMStamp = DateTime.MinValue;
94 }
95 private Dictionary<UUID, GroupRequestIDInfo> m_clientRequestIDInfo = new Dictionary<UUID, GroupRequestIDInfo>();
96 private const int m_clientRequestIDFlushTimeOut = 300000; // Every 5 minutes
97 private Timer m_clientRequestIDFlushTimer = new Timer();
98
99
100 // Configuration settings
101 private bool m_groupsEnabled = false;
102 private bool m_groupNoticesEnabled = true;
103 private bool m_debugEnabled = true;
104
105 #region IRegionModuleBase Members
106
107 public void Initialise(IConfigSource config)
108 {
109 IConfig groupsConfig = config.Configs["Groups"];
110
111 if (groupsConfig == null)
112 {
113 // Do not run this module by default.
114 return;
115 }
116 else
117 {
118 m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false);
119 if (!m_groupsEnabled)
120 {
121 return;
122 }
123
124 if (groupsConfig.GetString("Module", "Default") != Name)
125 {
126 m_groupsEnabled = false;
127
128 return;
129 }
130
131 m_log.InfoFormat("[GROUPS]: Initializing {0}", this.Name);
132
133 m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true);
134 m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true);
135
136 m_clientRequestIDFlushTimer.Interval = m_clientRequestIDFlushTimeOut;
137 m_clientRequestIDFlushTimer.Elapsed += FlushClientRequestIDInfoCache;
138 m_clientRequestIDFlushTimer.AutoReset = true;
139 m_clientRequestIDFlushTimer.Start();
140 }
141 }
142
143 void FlushClientRequestIDInfoCache(object sender, ElapsedEventArgs e)
144 {
145 lock (m_clientRequestIDInfo)
146 {
147 TimeSpan cacheTimeout = new TimeSpan(0,0, m_clientRequestIDFlushTimeOut / 1000);
148 UUID[] CurrentKeys = new UUID[m_clientRequestIDInfo.Count];
149 foreach (UUID key in CurrentKeys)
150 {
151 if (DateTime.Now - m_clientRequestIDInfo[key].LastUsedTMStamp > cacheTimeout)
152 {
153 m_clientRequestIDInfo.Remove(key);
154 }
155 }
156 }
157 }
158
159 public void AddRegion(Scene scene)
160 {
161 if (m_groupsEnabled)
162 scene.RegisterModuleInterface<IGroupsModule>(this);
163 }
164
165 public void RegionLoaded(Scene scene)
166 {
167 if (!m_groupsEnabled)
168 return;
169
170 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
171
172 if (m_groupData == null)
173 {
174 m_groupData = scene.RequestModuleInterface<IGroupsServicesConnector>();
175
176 // No Groups Service Connector, then nothing works...
177 if (m_groupData == null)
178 {
179 m_groupsEnabled = false;
180 m_log.Error("[GROUPS]: Could not get IGroupsServicesConnector");
181 Close();
182 return;
183 }
184 }
185
186 if (m_msgTransferModule == null)
187 {
188 m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
189
190 // No message transfer module, no notices, group invites, rejects, ejects, etc
191 if (m_msgTransferModule == null)
192 {
193 m_groupsEnabled = false;
194 m_log.Error("[GROUPS]: Could not get MessageTransferModule");
195 Close();
196 return;
197 }
198 }
199
200 lock (m_sceneList)
201 {
202 m_sceneList.Add(scene);
203 }
204
205 scene.EventManager.OnNewClient += OnNewClient;
206 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
207
208 // The InstantMessageModule itself doesn't do this,
209 // so lets see if things explode if we don't do it
210 // scene.EventManager.OnClientClosed += OnClientClosed;
211
212 }
213
214 public void RemoveRegion(Scene scene)
215 {
216 if (!m_groupsEnabled)
217 return;
218
219 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
220
221 lock (m_sceneList)
222 {
223 m_sceneList.Remove(scene);
224 }
225 }
226
227 public void Close()
228 {
229 if (!m_groupsEnabled)
230 return;
231
232 if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down Groups module.");
233
234 m_clientRequestIDFlushTimer.Stop();
235 }
236
237 public Type ReplacableInterface
238 {
239 get { return null; }
240 }
241
242 public string Name
243 {
244 get { return "GroupsModule"; }
245 }
246
247 #endregion
248
249 #region ISharedRegionModule Members
250
251 public void PostInitialise()
252 {
253 // NoOp
254 }
255
256 #endregion
257
258 #region EventHandlers
259 private void OnNewClient(IClientAPI client)
260 {
261 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
262
263 client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest;
264 client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest;
265 client.OnDirFindQuery += OnDirFindQuery;
266 client.OnRequestAvatarProperties += OnRequestAvatarProperties;
267
268 // Used for Notices and Group Invites/Accept/Reject
269 client.OnInstantMessage += OnInstantMessage;
270
271 lock (m_clientRequestIDInfo)
272 {
273 if (m_clientRequestIDInfo.ContainsKey(client.AgentId))
274 {
275 // flush any old RequestID information
276 m_clientRequestIDInfo.Remove(client.AgentId);
277 }
278 }
279 SendAgentGroupDataUpdate(client, client.AgentId);
280 }
281
282 private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID)
283 {
284 GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetClientGroupRequestID(remoteClient), avatarID).ToArray();
285 remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups);
286 }
287
288 /*
289 * This becomes very problematic in a shared module. In a shared module you may have more then one
290 * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections.
291 * The OnClientClosed event does not provide anything to indicate which one of those should be closed
292 * nor does it provide what scene it was from so that the specific reference can be looked up.
293 * The InstantMessageModule.cs does not currently worry about unregistering the handles,
294 * and it should be an issue, since it's the client that references us not the other way around
295 * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed
296 private void OnClientClosed(UUID AgentId)
297 {
298 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
299
300 lock (m_ActiveClients)
301 {
302 if (m_ActiveClients.ContainsKey(AgentId))
303 {
304 IClientAPI client = m_ActiveClients[AgentId];
305 client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
306 client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest;
307 client.OnDirFindQuery -= OnDirFindQuery;
308 client.OnInstantMessage -= OnInstantMessage;
309
310 m_ActiveClients.Remove(AgentId);
311 }
312 else
313 {
314 if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Client closed that wasn't registered here.");
315 }
316
317
318 }
319 }
320 */
321
322
323 void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart)
324 {
325 if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups)
326 {
327 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})", System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart);
328
329 // TODO: This currently ignores pretty much all the query flags including Mature and sort order
330 remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetClientGroupRequestID(remoteClient), queryText).ToArray());
331 }
332
333 }
334
335 private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
336 {
337 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
338
339 UUID activeGroupID = UUID.Zero;
340 string activeGroupTitle = string.Empty;
341 string activeGroupName = string.Empty;
342 ulong activeGroupPowers = (ulong)GroupPowers.None;
343
344 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetClientGroupRequestID(remoteClient), dataForAgentID);
345 if (membership != null)
346 {
347 activeGroupID = membership.GroupID;
348 activeGroupTitle = membership.GroupTitle;
349 activeGroupPowers = membership.GroupPowers;
350 }
351
352 SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle);
353
354 SendScenePresenceUpdate(dataForAgentID, activeGroupTitle);
355 }
356
357 private void HandleUUIDGroupNameRequest(UUID GroupID,IClientAPI remoteClient)
358 {
359 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
360
361 string GroupName;
362
363 GroupRecord group = m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), GroupID, null);
364 if (group != null)
365 {
366 GroupName = group.GroupName;
367 }
368 else
369 {
370 GroupName = "Unknown";
371 }
372
373 remoteClient.SendGroupNameReply(GroupID, GroupName);
374 }
375
376 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
377 {
378 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
379
380 // Group invitations
381 if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
382 {
383 UUID inviteID = new UUID(im.imSessionID);
384 GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID);
385
386 if (inviteInfo == null)
387 {
388 if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Received an Invite IM for an invite that does not exist {0}.", inviteID);
389 return;
390 }
391
392 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID);
393
394 UUID fromAgentID = new UUID(im.fromAgentID);
395 if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID))
396 {
397 // Accept
398 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept)
399 {
400 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received an accept invite notice.");
401
402 // and the sessionid is the role
403 m_groupData.AddAgentToGroup(GetClientGroupRequestID(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID);
404
405 GridInstantMessage msg = new GridInstantMessage();
406 msg.imSessionID = UUID.Zero.Guid;
407 msg.fromAgentID = UUID.Zero.Guid;
408 msg.toAgentID = inviteInfo.AgentID.Guid;
409 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
410 msg.fromAgentName = "Groups";
411 msg.message = string.Format("You have been added to the group.");
412 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox;
413 msg.fromGroup = false;
414 msg.offline = (byte)0;
415 msg.ParentEstateID = 0;
416 msg.Position = Vector3.Zero;
417 msg.RegionID = UUID.Zero.Guid;
418 msg.binaryBucket = new byte[0];
419
420 OutgoingInstantMessage(msg, inviteInfo.AgentID);
421
422 UpdateAllClientsWithGroupInfo(inviteInfo.AgentID);
423
424 // TODO: If the inviter is still online, they need an agent dataupdate
425 // and maybe group membership updates for the invitee
426
427 m_groupData.RemoveAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID);
428 }
429
430 // Reject
431 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)
432 {
433 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received a reject invite notice.");
434 m_groupData.RemoveAgentToGroupInvite(GetClientGroupRequestID(remoteClient), inviteID);
435 }
436 }
437 }
438
439 // Group notices
440 if ((im.dialog == (byte)InstantMessageDialog.GroupNotice))
441 {
442 if (!m_groupNoticesEnabled)
443 {
444 return;
445 }
446
447 UUID GroupID = new UUID(im.toAgentID);
448 if (m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), GroupID, null) != null)
449 {
450 UUID NoticeID = UUID.Random();
451 string Subject = im.message.Substring(0, im.message.IndexOf('|'));
452 string Message = im.message.Substring(Subject.Length + 1);
453
454 byte[] bucket;
455
456 if ((im.binaryBucket.Length == 1) && (im.binaryBucket[0] == 0))
457 {
458 bucket = new byte[19];
459 bucket[0] = 0; //dunno
460 bucket[1] = 0; //dunno
461 GroupID.ToBytes(bucket, 2);
462 bucket[18] = 0; //dunno
463 }
464 else
465 {
466 string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
467 binBucket = binBucket.Remove(0, 14).Trim();
468 if (m_debugEnabled)
469 {
470 m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket);
471
472 OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket);
473
474 foreach (string key in binBucketOSD.Keys)
475 {
476 m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString());
477 }
478 }
479
480 // treat as if no attachment
481 bucket = new byte[19];
482 bucket[0] = 0; //dunno
483 bucket[1] = 0; //dunno
484 GroupID.ToBytes(bucket, 2);
485 bucket[18] = 0; //dunno
486 }
487
488 m_groupData.AddGroupNotice(GetClientGroupRequestID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket);
489 if (OnNewGroupNotice != null)
490 {
491 OnNewGroupNotice(GroupID, NoticeID);
492 }
493
494 // Send notice out to everyone that wants notices
495 foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetClientGroupRequestID(remoteClient), GroupID))
496 {
497 if (member.AcceptNotices)
498 {
499 // Build notice IIM
500 GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
501
502 msg.toAgentID = member.AgentID.Guid;
503 OutgoingInstantMessage(msg, member.AgentID);
504 }
505 }
506 }
507 }
508
509 // Interop, received special 210 code for ejecting a group member
510 // this only works within the comms servers domain, and won't work hypergrid
511 // TODO:FIXME: Use a presense server of some kind to find out where the
512 // client actually is, and try contacting that region directly to notify them,
513 // or provide the notification via xmlrpc update queue
514 if ((im.dialog == 210))
515 {
516 // This is sent from the region that the ejectee was ejected from
517 // if it's being delivered here, then the ejectee is here
518 // so we need to send local updates to the agent.
519
520 UUID ejecteeID = new UUID(im.toAgentID);
521
522 im.dialog = (byte)InstantMessageDialog.MessageFromAgent;
523 OutgoingInstantMessage(im, ejecteeID);
524
525 IClientAPI ejectee = GetActiveClient(ejecteeID);
526 if (ejectee != null)
527 {
528 UUID groupID = new UUID(im.fromAgentID);
529 ejectee.SendAgentDropGroup(groupID);
530 }
531 }
532 }
533
534 private void OnGridInstantMessage(GridInstantMessage msg)
535 {
536 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
537
538 // Trigger the above event handler
539 OnInstantMessage(null, msg);
540
541 // If a message from a group arrives here, it may need to be forwarded to a local client
542 if (msg.fromGroup == true)
543 {
544 switch (msg.dialog)
545 {
546 case (byte)InstantMessageDialog.GroupInvitation:
547 case (byte)InstantMessageDialog.GroupNotice:
548 UUID toAgentID = new UUID(msg.toAgentID);
549 IClientAPI localClient = GetActiveClient(toAgentID);
550 if (localClient != null)
551 {
552 localClient.SendInstantMessage(msg);
553 }
554 break;
555 }
556 }
557 }
558
559 #endregion
560
561 #region IGroupsModule Members
562
563 public event NewGroupNotice OnNewGroupNotice;
564
565 public GroupRecord GetGroupRecord(UUID GroupID)
566 {
567 return m_groupData.GetGroupRecord(null, GroupID, null);
568 }
569
570 public void ActivateGroup(IClientAPI remoteClient, UUID groupID)
571 {
572 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
573
574 m_groupData.SetAgentActiveGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID);
575
576 // Changing active group changes title, active powers, all kinds of things
577 // anyone who is in any region that can see this client, should probably be
578 // updated with new group info. At a minimum, they should get ScenePresence
579 // updated with new title.
580 UpdateAllClientsWithGroupInfo(remoteClient.AgentId);
581 }
582
583 /// <summary>
584 /// Get the Role Titles for an Agent, for a specific group
585 /// </summary>
586 public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
587 {
588 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
589
590 GroupRequestID grID = GetClientGroupRequestID(remoteClient);
591
592 List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(grID, remoteClient.AgentId, groupID);
593 GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(grID, remoteClient.AgentId, groupID);
594
595 List<GroupTitlesData> titles = new List<GroupTitlesData>();
596 foreach (GroupRolesData role in agentRoles)
597 {
598 GroupTitlesData title = new GroupTitlesData();
599 title.Name = role.Name;
600 if (agentMembership != null)
601 {
602 title.Selected = agentMembership.ActiveRole == role.RoleID;
603 }
604 title.UUID = role.RoleID;
605
606 titles.Add(title);
607 }
608
609 return titles;
610 }
611
612 public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
613 {
614 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
615
616 List<GroupMembersData> data = m_groupData.GetGroupMembers(GetClientGroupRequestID(remoteClient), groupID);
617 if (m_debugEnabled)
618 {
619 foreach (GroupMembersData member in data)
620 {
621 m_log.DebugFormat("[GROUPS]: {0} {1}", member.AgentID, member.Title);
622 }
623 }
624
625 return data;
626
627 }
628
629 public List<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
630 {
631 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
632
633 List<GroupRolesData> data = m_groupData.GetGroupRoles(GetClientGroupRequestID(remoteClient), groupID);
634
635 if (m_debugEnabled)
636 {
637 foreach (GroupRolesData member in data)
638 {
639 m_log.DebugFormat("[GROUPS]: {0} {1}", member.Title, member.Members);
640 }
641 }
642
643 return data;
644
645 }
646
647 public List<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
648 {
649 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
650
651 List<GroupRoleMembersData> data = m_groupData.GetGroupRoleMembers(GetClientGroupRequestID(remoteClient), groupID);
652
653 if (m_debugEnabled)
654 {
655 foreach (GroupRoleMembersData member in data)
656 {
657 m_log.DebugFormat("[GROUPS]: Av: {0} Role: {1}", member.MemberID, member.RoleID);
658 }
659 }
660
661 return data;
662
663
664 }
665
666 public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID)
667 {
668 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
669
670 GroupProfileData profile = new GroupProfileData();
671
672 GroupRequestID grID = GetClientGroupRequestID(remoteClient);
673
674 GroupRecord groupInfo = m_groupData.GetGroupRecord(GetClientGroupRequestID(remoteClient), groupID, null);
675 if (groupInfo != null)
676 {
677 profile.AllowPublish = groupInfo.AllowPublish;
678 profile.Charter = groupInfo.Charter;
679 profile.FounderID = groupInfo.FounderID;
680 profile.GroupID = groupID;
681 profile.GroupMembershipCount = m_groupData.GetGroupMembers(grID, groupID).Count;
682 profile.GroupRolesCount = m_groupData.GetGroupRoles(grID, groupID).Count;
683 profile.InsigniaID = groupInfo.GroupPicture;
684 profile.MaturePublish = groupInfo.MaturePublish;
685 profile.MembershipFee = groupInfo.MembershipFee;
686 profile.Money = 0; // TODO: Get this from the currency server?
687 profile.Name = groupInfo.GroupName;
688 profile.OpenEnrollment = groupInfo.OpenEnrollment;
689 profile.OwnerRole = groupInfo.OwnerRoleID;
690 profile.ShowInList = groupInfo.ShowInList;
691 }
692
693 GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(grID, remoteClient.AgentId, groupID);
694 if (memberInfo != null)
695 {
696 profile.MemberTitle = memberInfo.GroupTitle;
697 profile.PowersMask = memberInfo.GroupPowers;
698 }
699
700 return profile;
701 }
702
703 public GroupMembershipData[] GetMembershipData(UUID agentID)
704 {
705 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
706
707 return m_groupData.GetAgentGroupMemberships(null, agentID).ToArray();
708 }
709
710 public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
711 {
712 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
713
714 return m_groupData.GetAgentGroupMembership(null, agentID, 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 // TODO: Security Check?
722
723 m_groupData.UpdateGroup(GetClientGroupRequestID(remoteClient), groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
724 }
725
726 public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile)
727 {
728 // TODO: Security Check?
729 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
730
731 m_groupData.SetAgentGroupInfo(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID, acceptNotices, listInProfile);
732 }
733
734 public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
735 {
736 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
737
738 GroupRequestID grID = GetClientGroupRequestID(remoteClient);
739
740 if (m_groupData.GetGroupRecord(grID, 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 // is there is a money module present ?
746 IMoneyModule money=remoteClient.Scene.RequestModuleInterface<IMoneyModule>();
747 if (money != null)
748 {
749 // do the transaction, that is if the agent has got sufficient funds
750 if (!money.GroupCreationCovered(remoteClient)) {
751 remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got issuficient funds to create a group.");
752 return UUID.Zero;
753 }
754 money.ApplyGroupCreationCharge(remoteClient.AgentId);
755 }
756 UUID groupID = m_groupData.CreateGroup(grID, name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, remoteClient.AgentId);
757
758 remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly");
759
760 // Update the founder with new group information.
761 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
762
763 return groupID;
764 }
765
766 public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID)
767 {
768 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
769
770 // ToDo: check if agent is a member of group and is allowed to see notices?
771
772 return m_groupData.GetGroupNotices(GetClientGroupRequestID(remoteClient), groupID).ToArray();
773 }
774
775 /// <summary>
776 /// Get the title of the agent's current role.
777 /// </summary>
778 public string GetGroupTitle(UUID avatarID)
779 {
780 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
781
782 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(null, avatarID);
783 if (membership != null)
784 {
785 return membership.GroupTitle;
786 }
787 return string.Empty;
788 }
789
790 /// <summary>
791 /// Change the current Active Group Role for Agent
792 /// </summary>
793 public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID)
794 {
795 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
796
797 m_groupData.SetAgentActiveGroupRole(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID, titleRoleID);
798
799 // TODO: Not sure what all is needed here, but if the active group role change is for the group
800 // the client currently has set active, then we need to do a scene presence update too
801 // if (m_groupData.GetAgentActiveMembership(remoteClient.AgentId).GroupID == GroupID)
802
803 UpdateAllClientsWithGroupInfo(remoteClient.AgentId);
804 }
805
806
807 public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType)
808 {
809 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
810
811 // TODO: Security Checks?
812
813 GroupRequestID grID = GetClientGroupRequestID(remoteClient);
814
815 switch ((OpenMetaverse.GroupRoleUpdate)updateType)
816 {
817 case OpenMetaverse.GroupRoleUpdate.Create:
818 m_groupData.AddGroupRole(grID, groupID, UUID.Random(), name, description, title, powers);
819 break;
820
821 case OpenMetaverse.GroupRoleUpdate.Delete:
822 m_groupData.RemoveGroupRole(grID, groupID, roleID);
823 break;
824
825 case OpenMetaverse.GroupRoleUpdate.UpdateAll:
826 case OpenMetaverse.GroupRoleUpdate.UpdateData:
827 case OpenMetaverse.GroupRoleUpdate.UpdatePowers:
828 m_groupData.UpdateGroupRole(grID, groupID, roleID, name, description, title, powers);
829 break;
830
831 case OpenMetaverse.GroupRoleUpdate.NoUpdate:
832 default:
833 // No Op
834 break;
835
836 }
837
838 // TODO: This update really should send out updates for everyone in the role that just got changed.
839 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
840 }
841
842 public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes)
843 {
844 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
845 // Todo: Security check
846
847 GroupRequestID grID = GetClientGroupRequestID(remoteClient);
848
849 switch (changes)
850 {
851 case 0:
852 // Add
853 m_groupData.AddAgentToGroupRole(grID, memberID, groupID, roleID);
854
855 break;
856 case 1:
857 // Remove
858 m_groupData.RemoveAgentFromGroupRole(grID, memberID, groupID, roleID);
859
860 break;
861 default:
862 m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes);
863 break;
864 }
865
866 // TODO: This update really should send out updates for everyone in the role that just got changed.
867 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
868 }
869
870 public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID)
871 {
872 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
873
874 GroupRequestID grID = GetClientGroupRequestID(remoteClient);
875
876 GroupNoticeInfo data = m_groupData.GetGroupNotice(grID, groupNoticeID);
877
878 if (data != null)
879 {
880 GroupRecord groupInfo = m_groupData.GetGroupRecord(grID, data.GroupID, null);
881
882 GridInstantMessage msg = new GridInstantMessage();
883 msg.imSessionID = UUID.Zero.Guid;
884 msg.fromAgentID = data.GroupID.Guid;
885 msg.toAgentID = remoteClient.AgentId.Guid;
886 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
887 msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName;
888 msg.message = data.noticeData.Subject + "|" + data.Message;
889 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested;
890 msg.fromGroup = true;
891 msg.offline = (byte)0;
892 msg.ParentEstateID = 0;
893 msg.Position = Vector3.Zero;
894 msg.RegionID = UUID.Zero.Guid;
895 msg.binaryBucket = data.BinaryBucket;
896
897 OutgoingInstantMessage(msg, remoteClient.AgentId);
898 }
899
900 }
901
902 public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
903 {
904 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
905
906 GridInstantMessage msg = new GridInstantMessage();
907 msg.imSessionID = UUID.Zero.Guid;
908 msg.toAgentID = agentID.Guid;
909 msg.dialog = dialog;
910 // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice;
911 msg.fromGroup = true;
912 msg.offline = (byte)1; // Allow this message to be stored for offline use
913 msg.ParentEstateID = 0;
914 msg.Position = Vector3.Zero;
915 msg.RegionID = UUID.Zero.Guid;
916
917 GroupNoticeInfo info = m_groupData.GetGroupNotice(null, groupNoticeID);
918 if (info != null)
919 {
920 msg.fromAgentID = info.GroupID.Guid;
921 msg.timestamp = info.noticeData.Timestamp;
922 msg.fromAgentName = info.noticeData.FromName;
923 msg.message = info.noticeData.Subject + "|" + info.Message;
924 msg.binaryBucket = info.BinaryBucket;
925 }
926 else
927 {
928 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID);
929 msg.fromAgentID = UUID.Zero.Guid;
930 msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ;
931 msg.fromAgentName = string.Empty;
932 msg.message = string.Empty;
933 msg.binaryBucket = new byte[0];
934 }
935
936 return msg;
937 }
938
939 public void SendAgentGroupDataUpdate(IClientAPI remoteClient)
940 {
941 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
942
943 // Send agent information about his groups
944 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
945 }
946
947 public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID)
948 {
949 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
950
951 // Should check to see if OpenEnrollment, or if there's an outstanding invitation
952 m_groupData.AddAgentToGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID, UUID.Zero);
953
954 remoteClient.SendJoinGroupReply(groupID, true);
955
956 // Should this send updates to everyone in the group?
957 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
958 }
959
960 public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID)
961 {
962 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
963
964 m_groupData.RemoveAgentFromGroup(GetClientGroupRequestID(remoteClient), remoteClient.AgentId, groupID);
965
966 remoteClient.SendLeaveGroupReply(groupID, true);
967
968 remoteClient.SendAgentDropGroup(groupID);
969
970 // SL sends out notifcations to the group messaging session that the person has left
971 // Should this also update everyone who is in the group?
972 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
973 }
974
975 public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID)
976 {
977 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
978
979 GroupRequestID grID = GetClientGroupRequestID(remoteClient);
980
981 // Todo: Security check?
982 m_groupData.RemoveAgentFromGroup(grID, ejecteeID, groupID);
983
984 remoteClient.SendEjectGroupMemberReply(remoteClient.AgentId, groupID, true);
985
986 GroupRecord groupInfo = m_groupData.GetGroupRecord(grID, groupID, null);
987 UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(ejecteeID);
988
989 if ((groupInfo == null) || (userProfile == null))
990 {
991 return;
992 }
993
994
995 // Send Message to Ejectee
996 GridInstantMessage msg = new GridInstantMessage();
997
998 msg.imSessionID = UUID.Zero.Guid;
999 msg.fromAgentID = remoteClient.AgentId.Guid;
1000 // msg.fromAgentID = info.GroupID;
1001 msg.toAgentID = ejecteeID.Guid;
1002 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1003 msg.timestamp = 0;
1004 msg.fromAgentName = remoteClient.Name;
1005 msg.message = string.Format("You have been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName);
1006 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent;
1007 msg.fromGroup = false;
1008 msg.offline = (byte)0;
1009 msg.ParentEstateID = 0;
1010 msg.Position = Vector3.Zero;
1011 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid;
1012 msg.binaryBucket = new byte[0];
1013 OutgoingInstantMessage(msg, ejecteeID);
1014
1015
1016 // Message to ejector
1017 // Interop, received special 210 code for ejecting a group member
1018 // this only works within the comms servers domain, and won't work hypergrid
1019 // TODO:FIXME: Use a presense server of some kind to find out where the
1020 // client actually is, and try contacting that region directly to notify them,
1021 // or provide the notification via xmlrpc update queue
1022
1023 msg = new GridInstantMessage();
1024 msg.imSessionID = UUID.Zero.Guid;
1025 msg.fromAgentID = remoteClient.AgentId.Guid;
1026 msg.toAgentID = remoteClient.AgentId.Guid;
1027 msg.timestamp = 0;
1028 msg.fromAgentName = remoteClient.Name;
1029 if (userProfile != null)
1030 {
1031 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName, userProfile.Name);
1032 }
1033 else
1034 {
1035 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName, "Unknown member");
1036 }
1037 msg.dialog = (byte)210; //interop
1038 msg.fromGroup = false;
1039 msg.offline = (byte)0;
1040 msg.ParentEstateID = 0;
1041 msg.Position = Vector3.Zero;
1042 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid;
1043 msg.binaryBucket = new byte[0];
1044 OutgoingInstantMessage(msg, remoteClient.AgentId);
1045
1046
1047 // SL sends out messages to everyone in the group
1048 // Who all should receive updates and what should they be updated with?
1049 UpdateAllClientsWithGroupInfo(ejecteeID);
1050 }
1051
1052 public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID)
1053 {
1054 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1055
1056 // Todo: Security check, probably also want to send some kind of notification
1057 UUID InviteID = UUID.Random();
1058 GroupRequestID grid = GetClientGroupRequestID(remoteClient);
1059
1060 m_groupData.AddAgentToGroupInvite(grid, InviteID, groupID, roleID, invitedAgentID);
1061
1062 // Check to see if the invite went through, if it did not then it's possible
1063 // the remoteClient did not validate or did not have permission to invite.
1064 GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(grid, InviteID);
1065
1066 if (inviteInfo != null)
1067 {
1068 if (m_msgTransferModule != null)
1069 {
1070 Guid inviteUUID = InviteID.Guid;
1071
1072 GridInstantMessage msg = new GridInstantMessage();
1073
1074 msg.imSessionID = inviteUUID;
1075
1076 // msg.fromAgentID = remoteClient.AgentId.Guid;
1077 msg.fromAgentID = groupID.Guid;
1078 msg.toAgentID = invitedAgentID.Guid;
1079 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1080 msg.timestamp = 0;
1081 msg.fromAgentName = remoteClient.Name;
1082 msg.message = string.Format("{0} has invited you to join a group. There is no cost to join this group.", remoteClient.Name);
1083 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
1084 msg.fromGroup = true;
1085 msg.offline = (byte)0;
1086 msg.ParentEstateID = 0;
1087 msg.Position = Vector3.Zero;
1088 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid;
1089 msg.binaryBucket = new byte[20];
1090
1091 OutgoingInstantMessage(msg, invitedAgentID);
1092 }
1093 }
1094 }
1095
1096 #endregion
1097
1098 #region Client/Update Tools
1099
1100 /// <summary>
1101 /// Try to find an active IClientAPI reference for agentID giving preference to root connections
1102 /// </summary>
1103 private IClientAPI GetActiveClient(UUID agentID)
1104 {
1105 IClientAPI child = null;
1106
1107 // Try root avatar first
1108 foreach (Scene scene in m_sceneList)
1109 {
1110 if (scene.Entities.ContainsKey(agentID) &&
1111 scene.Entities[agentID] is ScenePresence)
1112 {
1113 ScenePresence user = (ScenePresence)scene.Entities[agentID];
1114 if (!user.IsChildAgent)
1115 {
1116 return user.ControllingClient;
1117 }
1118 else
1119 {
1120 child = user.ControllingClient;
1121 }
1122 }
1123 }
1124
1125 // If we didn't find a root, then just return whichever child we found, or null if none
1126 return child;
1127 }
1128
1129 private GroupRequestID GetClientGroupRequestID(IClientAPI client)
1130 {
1131 if (client == null)
1132 {
1133 return new GroupRequestID();
1134 }
1135
1136 lock (m_clientRequestIDInfo)
1137 {
1138 if (!m_clientRequestIDInfo.ContainsKey(client.AgentId))
1139 {
1140 GroupRequestIDInfo info = new GroupRequestIDInfo();
1141 info.RequestID.AgentID = client.AgentId;
1142 info.RequestID.SessionID = client.SessionId;
1143
1144 UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(client.AgentId);
1145 if (userProfile == null)
1146 {
1147 // This should be impossible. If I've been passed a reference to a client
1148 // that client should be registered with the UserService. So something
1149 // is horribly wrong somewhere.
1150
1151 m_log.WarnFormat("[GROUPS]: Could not find a user profile for {0} / {1}", client.Name, client.AgentId);
1152
1153 // Default to local user service and hope for the best?
1154 info.RequestID.UserServiceURL = m_sceneList[0].CommsManager.NetworkServersInfo.UserURL;
1155
1156 }
1157 else if (userProfile is ForeignUserProfileData)
1158 {
1159 // They aren't from around here
1160 ForeignUserProfileData fupd = (ForeignUserProfileData)userProfile;
1161 info.RequestID.UserServiceURL = fupd.UserServerURI;
1162 }
1163 else
1164 {
1165 // They're a local user, use this:
1166 info.RequestID.UserServiceURL = m_sceneList[0].CommsManager.NetworkServersInfo.UserURL;
1167 }
1168
1169 m_clientRequestIDInfo.Add(client.AgentId, info);
1170 }
1171
1172 m_clientRequestIDInfo[client.AgentId].LastUsedTMStamp = DateTime.Now;
1173 }
1174 return m_clientRequestIDInfo[client.AgentId].RequestID;
1175 }
1176
1177 /// <summary>
1178 /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'.
1179 /// </summary>
1180 private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data)
1181 {
1182 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1183
1184 OSDArray AgentData = new OSDArray(1);
1185 OSDMap AgentDataMap = new OSDMap(1);
1186 AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID));
1187 AgentData.Add(AgentDataMap);
1188
1189
1190 OSDArray GroupData = new OSDArray(data.Length);
1191 OSDArray NewGroupData = new OSDArray(data.Length);
1192
1193 foreach (GroupMembershipData membership in data)
1194 {
1195 OSDMap GroupDataMap = new OSDMap(6);
1196 OSDMap NewGroupDataMap = new OSDMap(1);
1197
1198 GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID));
1199 GroupDataMap.Add("GroupPowers", OSD.FromBinary(membership.GroupPowers));
1200 GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices));
1201 GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture));
1202 GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution));
1203 GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName));
1204 NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile));
1205
1206 GroupData.Add(GroupDataMap);
1207 NewGroupData.Add(NewGroupDataMap);
1208 }
1209
1210 OSDMap llDataStruct = new OSDMap(3);
1211 llDataStruct.Add("AgentData", AgentData);
1212 llDataStruct.Add("GroupData", GroupData);
1213 llDataStruct.Add("NewGroupData", NewGroupData);
1214
1215 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
1216
1217 if (queue != null)
1218 {
1219 queue.Enqueue(EventQueueHelper.buildEvent("AgentGroupDataUpdate", llDataStruct), remoteClient.AgentId);
1220 }
1221
1222 }
1223
1224 private void SendScenePresenceUpdate(UUID AgentID, string Title)
1225 {
1226 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Updating scene title for {0} with title: {1}", AgentID, Title);
1227
1228 ScenePresence presence = null;
1229 lock (m_sceneList)
1230 {
1231 foreach (Scene scene in m_sceneList)
1232 {
1233 presence = scene.GetScenePresence(AgentID);
1234 if (presence != null)
1235 {
1236 presence.Grouptitle = Title;
1237
1238 // FixMe: Ter suggests a "Schedule" method that I can't find.
1239 presence.SendFullUpdateToAllClients();
1240 }
1241 }
1242 }
1243 }
1244
1245 /// <summary>
1246 /// Send updates to all clients who might be interested in groups data for dataForClientID
1247 /// </summary>
1248 private void UpdateAllClientsWithGroupInfo(UUID dataForClientID)
1249 {
1250 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1251
1252 // TODO: Probably isn't nessesary to update every client in every scene.
1253 // Need to examine client updates and do only what's nessesary.
1254 lock (m_sceneList)
1255 {
1256 foreach (Scene scene in m_sceneList)
1257 {
1258 scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); });
1259 }
1260 }
1261 }
1262
1263 /// <summary>
1264 /// Update remoteClient with group information about dataForAgentID
1265 /// </summary>
1266 private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID)
1267 {
1268 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name);
1269
1270 // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
1271
1272 OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero);
1273
1274
1275 // Need to send a group membership update to the client
1276 // UDP version doesn't seem to behave nicely. But we're going to send it out here
1277 // with an empty group membership to hopefully remove groups being displayed due
1278 // to the core Groups Stub
1279 remoteClient.SendGroupMembership(new GroupMembershipData[0]);
1280
1281 GroupMembershipData[] membershipData = m_groupData.GetAgentGroupMemberships(GetClientGroupRequestID(remoteClient), dataForAgentID).ToArray();
1282
1283 SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipData);
1284 remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipData);
1285
1286 }
1287
1288 private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle)
1289 {
1290 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1291
1292 // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
1293 UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(dataForAgentID);
1294 string firstname, lastname;
1295 if (userProfile != null)
1296 {
1297 firstname = userProfile.FirstName;
1298 lastname = userProfile.SurName;
1299 }
1300 else
1301 {
1302 firstname = "Unknown";
1303 lastname = "Unknown";
1304 }
1305
1306 remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname,
1307 lastname, activeGroupPowers, activeGroupName,
1308 activeGroupTitle);
1309 }
1310
1311 #endregion
1312
1313 #region IM Backed Processes
1314
1315 private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo)
1316 {
1317 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1318
1319 IClientAPI localClient = GetActiveClient(msgTo);
1320 if (localClient != null)
1321 {
1322 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is local, delivering directly", localClient.Name);
1323 localClient.SendInstantMessage(msg);
1324 }
1325 else
1326 {
1327 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo);
1328 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Message Sent: {0}", success?"Succeeded":"Failed"); });
1329 }
1330 }
1331
1332 public void NotifyChange(UUID groupID)
1333 {
1334 // Notify all group members of a chnge in group roles and/or
1335 // permissions
1336 //
1337 }
1338
1339 #endregion
1340 }
1341
1342}