aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups
diff options
context:
space:
mode:
authorUbitUmarov2015-09-01 11:43:07 +0100
committerUbitUmarov2015-09-01 11:43:07 +0100
commitfb78b182520fc9bb0f971afd0322029c70278ea6 (patch)
treeb4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups
parentlixo (diff)
parentMantis #7713: fixed bug introduced by 1st MOSES patch. (diff)
downloadopensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.zip
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.gz
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.bz2
opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.xz
Merge remote-tracking branch 'os/master'
Diffstat (limited to 'OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups')
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs704
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs1554
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs121
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs1441
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs268
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs1174
6 files changed, 5262 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
new file mode 100644
index 0000000..e1b6abb
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
@@ -0,0 +1,704 @@
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.Linq;
31using System.Reflection;
32using log4net;
33using Mono.Addins;
34using Nini.Config;
35using OpenMetaverse;
36using OpenMetaverse.StructuredData;
37using OpenSim.Framework;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
42
43namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
44{
45 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsMessagingModule")]
46 public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule
47 {
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49
50 private List<Scene> m_sceneList = new List<Scene>();
51 private IPresenceService m_presenceService;
52
53 private IMessageTransferModule m_msgTransferModule = null;
54
55 private IGroupsServicesConnector m_groupData = null;
56
57 // Config Options
58 private bool m_groupMessagingEnabled;
59 private bool m_debugEnabled;
60
61 /// <summary>
62 /// If enabled, module only tries to send group IMs to online users by querying cached presence information.
63 /// </summary>
64 private bool m_messageOnlineAgentsOnly;
65
66 /// <summary>
67 /// Cache for online users.
68 /// </summary>
69 /// <remarks>
70 /// Group ID is key, presence information for online members is value.
71 /// Will only be non-null if m_messageOnlineAgentsOnly = true
72 /// We cache here so that group messages don't constantly have to re-request the online user list to avoid
73 /// attempted expensive sending of messages to offline users.
74 /// The tradeoff is that a user that comes online will not receive messages consistently from all other users
75 /// until caches have updated.
76 /// Therefore, we set the cache expiry to just 20 seconds.
77 /// </remarks>
78 private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
79
80 private int m_usersOnlineCacheExpirySeconds = 20;
81
82 #region Region Module interfaceBase Members
83
84 public void Initialise(IConfigSource config)
85 {
86 IConfig groupsConfig = config.Configs["Groups"];
87
88 if (groupsConfig == null)
89 {
90 // Do not run this module by default.
91 return;
92 }
93 else
94 {
95 // if groups aren't enabled, we're not needed.
96 // if we're not specified as the connector to use, then we're not wanted
97 if ((groupsConfig.GetBoolean("Enabled", false) == false)
98 || (groupsConfig.GetString("MessagingModule", "") != Name))
99 {
100 m_groupMessagingEnabled = false;
101 return;
102 }
103
104 m_groupMessagingEnabled = groupsConfig.GetBoolean("MessagingEnabled", true);
105
106 if (!m_groupMessagingEnabled)
107 {
108 return;
109 }
110
111 m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
112
113 if (m_messageOnlineAgentsOnly)
114 m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
115
116 m_debugEnabled = groupsConfig.GetBoolean("MessagingDebugEnabled", m_debugEnabled);
117 }
118
119 m_log.InfoFormat(
120 "[GROUPS-MESSAGING]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
121 m_messageOnlineAgentsOnly, m_debugEnabled);
122 }
123
124 public void AddRegion(Scene scene)
125 {
126 if (!m_groupMessagingEnabled)
127 return;
128
129 scene.RegisterModuleInterface<IGroupsMessagingModule>(this);
130
131 scene.AddCommand(
132 "Debug",
133 this,
134 "debug groups messaging verbose",
135 "debug groups messaging verbose <true|false>",
136 "This setting turns on very verbose groups messaging debugging",
137 HandleDebugGroupsMessagingVerbose);
138 }
139
140 public void RegionLoaded(Scene scene)
141 {
142 if (!m_groupMessagingEnabled)
143 return;
144
145 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
146
147 m_groupData = scene.RequestModuleInterface<IGroupsServicesConnector>();
148
149 // No groups module, no groups messaging
150 if (m_groupData == null)
151 {
152 m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled.");
153 Close();
154 m_groupMessagingEnabled = false;
155 return;
156 }
157
158 m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
159
160 // No message transfer module, no groups messaging
161 if (m_msgTransferModule == null)
162 {
163 m_log.Error("[GROUPS-MESSAGING]: Could not get MessageTransferModule");
164 Close();
165 m_groupMessagingEnabled = false;
166 return;
167 }
168
169 if (m_presenceService == null)
170 m_presenceService = scene.PresenceService;
171
172 m_sceneList.Add(scene);
173
174 scene.EventManager.OnNewClient += OnNewClient;
175 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
176 scene.EventManager.OnClientLogin += OnClientLogin;
177 }
178
179 public void RemoveRegion(Scene scene)
180 {
181 if (!m_groupMessagingEnabled)
182 return;
183
184 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
185
186 m_sceneList.Remove(scene);
187 }
188
189 public void Close()
190 {
191 if (!m_groupMessagingEnabled)
192 return;
193
194 if (m_debugEnabled) m_log.Debug("[GROUPS-MESSAGING]: Shutting down GroupsMessagingModule module.");
195
196 foreach (Scene scene in m_sceneList)
197 {
198 scene.EventManager.OnNewClient -= OnNewClient;
199 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
200 }
201
202 m_sceneList.Clear();
203
204 m_groupData = null;
205 m_msgTransferModule = null;
206 }
207
208 public Type ReplaceableInterface
209 {
210 get { return null; }
211 }
212
213 public string Name
214 {
215 get { return "GroupsMessagingModule"; }
216 }
217
218 #endregion
219
220 #region ISharedRegionModule Members
221
222 public void PostInitialise()
223 {
224 // NoOp
225 }
226
227 #endregion
228
229 private void HandleDebugGroupsMessagingVerbose(object modules, string[] args)
230 {
231 if (args.Length < 5)
232 {
233 MainConsole.Instance.Output("Usage: debug groups messaging verbose <true|false>");
234 return;
235 }
236
237 bool verbose = false;
238 if (!bool.TryParse(args[4], out verbose))
239 {
240 MainConsole.Instance.Output("Usage: debug groups messaging verbose <true|false>");
241 return;
242 }
243
244 m_debugEnabled = verbose;
245
246 MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
247 }
248
249 /// <summary>
250 /// Not really needed, but does confirm that the group exists.
251 /// </summary>
252 public bool StartGroupChatSession(UUID agentID, UUID groupID)
253 {
254 if (m_debugEnabled)
255 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
256
257 GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID, groupID, null);
258
259 if (groupInfo != null)
260 {
261 return true;
262 }
263 else
264 {
265 return false;
266 }
267 }
268
269 public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
270 {
271 SendMessageToGroup(im, groupID, new UUID(im.fromAgentID), null);
272 }
273
274 public void SendMessageToGroup(
275 GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func<GroupMembersData, bool> sendCondition)
276 {
277 int requestStartTick = Environment.TickCount;
278
279 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(sendingAgentForGroupCalls, groupID);
280 int groupMembersCount = groupMembers.Count;
281 HashSet<string> attemptDeliveryUuidSet = null;
282
283 if (m_messageOnlineAgentsOnly)
284 {
285 string[] t1 = groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()).ToArray();
286
287 // We cache in order not to overwhlem the presence service on large grids with many groups. This does
288 // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed.
289 // (assuming this is the same across all grid simulators).
290 PresenceInfo[] onlineAgents;
291 if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
292 {
293 onlineAgents = m_presenceService.GetAgents(t1);
294 m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
295 }
296
297 attemptDeliveryUuidSet
298 = new HashSet<string>(Array.ConvertAll<PresenceInfo, string>(onlineAgents, pi => pi.UserID));
299
300 //Array.ForEach<PresenceInfo>(onlineAgents, pi => attemptDeliveryUuidSet.Add(pi.UserID));
301
302 //groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
303
304 // if (m_debugEnabled)
305// m_log.DebugFormat(
306// "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members, {2} online",
307// groupID, groupMembersCount, groupMembers.Count());
308 }
309 else
310 {
311 attemptDeliveryUuidSet
312 = new HashSet<string>(groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()));
313
314 if (m_debugEnabled)
315 m_log.DebugFormat(
316 "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
317 groupID, groupMembers.Count);
318 }
319
320 foreach (GroupMembersData member in groupMembers)
321 {
322 if (sendCondition != null)
323 {
324 if (!sendCondition(member))
325 {
326 if (m_debugEnabled)
327 m_log.DebugFormat(
328 "[GROUPS-MESSAGING]: Not sending to {0} as they do not fulfill send condition",
329 member.AgentID);
330
331 continue;
332 }
333 }
334 else if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID))
335 {
336 // Don't deliver messages to people who have dropped this session
337 if (m_debugEnabled)
338 m_log.DebugFormat(
339 "[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID);
340
341 continue;
342 }
343
344 // Copy Message
345 GridInstantMessage msg = new GridInstantMessage();
346 msg.imSessionID = im.imSessionID;
347 msg.fromAgentName = im.fromAgentName;
348 msg.message = im.message;
349 msg.dialog = im.dialog;
350 msg.offline = im.offline;
351 msg.ParentEstateID = im.ParentEstateID;
352 msg.Position = im.Position;
353 msg.RegionID = im.RegionID;
354 msg.binaryBucket = im.binaryBucket;
355 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
356
357 msg.fromAgentID = im.fromAgentID;
358 msg.fromGroup = true;
359
360 msg.toAgentID = member.AgentID.Guid;
361
362 if (attemptDeliveryUuidSet.Contains(member.AgentID.ToString()))
363 {
364 IClientAPI client = GetActiveClient(member.AgentID);
365 if (client == null)
366 {
367 int startTick = Environment.TickCount;
368
369 // If they're not local, forward across the grid
370 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { });
371
372 if (m_debugEnabled)
373 m_log.DebugFormat(
374 "[GROUPS-MESSAGING]: Delivering to {0} via grid took {1} ms",
375 member.AgentID, Environment.TickCount - startTick);
376 }
377 else
378 {
379 int startTick = Environment.TickCount;
380
381 ProcessMessageFromGroupSession(msg, client);
382
383 // Deliver locally, directly
384 if (m_debugEnabled)
385 m_log.DebugFormat(
386 "[GROUPS-MESSAGING]: Delivering to {0} locally took {1} ms",
387 member.AgentID, Environment.TickCount - startTick);
388 }
389 }
390 else
391 {
392 int startTick = Environment.TickCount;
393
394 m_msgTransferModule.HandleUndeliverableMessage(msg, delegate(bool success) { });
395
396 if (m_debugEnabled)
397 m_log.DebugFormat(
398 "[GROUPS-MESSAGING]: Handling undeliverable message for {0} took {1} ms",
399 member.AgentID, Environment.TickCount - startTick);
400 }
401 }
402
403 if (m_debugEnabled)
404 m_log.DebugFormat(
405 "[GROUPS-MESSAGING]: Total SendMessageToGroup for group {0} with {1} members, {2} candidates for delivery took {3} ms",
406 groupID, groupMembersCount, attemptDeliveryUuidSet.Count(), Environment.TickCount - requestStartTick);
407 }
408
409 #region SimGridEventHandlers
410
411 void OnClientLogin(IClientAPI client)
412 {
413 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name);
414 }
415
416 private void OnNewClient(IClientAPI client)
417 {
418 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name);
419
420 client.OnInstantMessage += OnInstantMessage;
421 }
422
423 private void OnGridInstantMessage(GridInstantMessage msg)
424 {
425 // The instant message module will only deliver messages of dialog types:
426 // MessageFromAgent, StartTyping, StopTyping, MessageFromObject
427 //
428 // Any other message type will not be delivered to a client by the
429 // Instant Message Module
430
431 if (m_debugEnabled)
432 {
433 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
434
435 DebugGridInstantMessage(msg);
436 }
437
438 // Incoming message from a group
439 if ((msg.fromGroup == true) &&
440 ((msg.dialog == (byte)InstantMessageDialog.SessionSend)
441 || (msg.dialog == (byte)InstantMessageDialog.SessionAdd)
442 || (msg.dialog == (byte)InstantMessageDialog.SessionDrop)))
443 {
444 IClientAPI client = null;
445
446 if (msg.dialog == (byte)InstantMessageDialog.SessionSend)
447 {
448 client = GetActiveClient(new UUID(msg.toAgentID));
449
450 if (client != null)
451 {
452 if (m_debugEnabled)
453 m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} locally", client.Name);
454 }
455 else
456 {
457 m_log.WarnFormat("[GROUPS-MESSAGING]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
458
459 return;
460 }
461 }
462
463 ProcessMessageFromGroupSession(msg, client);
464 }
465 }
466
467 private void ProcessMessageFromGroupSession(GridInstantMessage msg, IClientAPI client)
468 {
469 if (m_debugEnabled)
470 m_log.DebugFormat(
471 "[GROUPS-MESSAGING]: Session message from {0} going to agent {1}, sessionID {2}, type {3}",
472 msg.fromAgentName, msg.toAgentID, msg.imSessionID, (InstantMessageDialog)msg.dialog);
473
474 UUID AgentID = new UUID(msg.fromAgentID);
475 UUID GroupID = new UUID(msg.imSessionID);
476
477 switch (msg.dialog)
478 {
479 case (byte)InstantMessageDialog.SessionAdd:
480 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
481 break;
482
483 case (byte)InstantMessageDialog.SessionDrop:
484 m_groupData.AgentDroppedFromGroupChatSession(AgentID, GroupID);
485 break;
486
487 case (byte)InstantMessageDialog.SessionSend:
488 if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID)
489 && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID, GroupID)
490 )
491 {
492 // Agent not in session and hasn't dropped from session
493 // Add them to the session for now, and Invite them
494 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
495
496 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
497 if (groupInfo != null)
498 {
499 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message");
500
501 // Force? open the group session dialog???
502 // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg);
503 IEventQueue eq = client.Scene.RequestModuleInterface<IEventQueue>();
504 eq.ChatterboxInvitation(
505 GroupID
506 , groupInfo.GroupName
507 , new UUID(msg.fromAgentID)
508 , msg.message
509 , new UUID(msg.toAgentID)
510 , msg.fromAgentName
511 , msg.dialog
512 , msg.timestamp
513 , msg.offline == 1
514 , (int)msg.ParentEstateID
515 , msg.Position
516 , 1
517 , new UUID(msg.imSessionID)
518 , msg.fromGroup
519 , Utils.StringToBytes(groupInfo.GroupName)
520 );
521
522 eq.ChatterBoxSessionAgentListUpdates(
523 new UUID(GroupID)
524 , new UUID(msg.fromAgentID)
525 , new UUID(msg.toAgentID)
526 , false //canVoiceChat
527 , false //isModerator
528 , false //text mute
529 );
530 }
531
532 break;
533 }
534 else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID))
535 {
536 // User hasn't dropped, so they're in the session,
537 // maybe we should deliver it.
538 client.SendInstantMessage(msg);
539 }
540
541 break;
542
543 default:
544 client.SendInstantMessage(msg);
545
546 break;;
547 }
548 }
549
550 #endregion
551
552 #region ClientEvents
553 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
554 {
555 if (m_debugEnabled)
556 {
557 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
558
559 DebugGridInstantMessage(im);
560 }
561
562 // Start group IM session
563 if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart))
564 {
565 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID);
566
567 UUID GroupID = new UUID(im.imSessionID);
568 UUID AgentID = new UUID(im.fromAgentID);
569
570 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
571
572 if (groupInfo != null)
573 {
574 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
575
576 ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID);
577
578 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
579 queue.ChatterBoxSessionAgentListUpdates(
580 GroupID
581 , AgentID
582 , new UUID(im.toAgentID)
583 , false //canVoiceChat
584 , false //isModerator
585 , false //text mute
586 );
587 }
588 }
589
590 // Send a message from locally connected client to a group
591 if ((im.dialog == (byte)InstantMessageDialog.SessionSend))
592 {
593 UUID GroupID = new UUID(im.imSessionID);
594 UUID AgentID = new UUID(im.fromAgentID);
595
596 if (m_debugEnabled)
597 m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString());
598
599 //If this agent is sending a message, then they want to be in the session
600 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
601
602 SendMessageToGroup(im, GroupID);
603 }
604 }
605
606 #endregion
607
608 void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID)
609 {
610 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
611
612 OSDMap moderatedMap = new OSDMap(4);
613 moderatedMap.Add("voice", OSD.FromBoolean(false));
614
615 OSDMap sessionMap = new OSDMap(4);
616 sessionMap.Add("moderated_mode", moderatedMap);
617 sessionMap.Add("session_name", OSD.FromString(groupName));
618 sessionMap.Add("type", OSD.FromInteger(0));
619 sessionMap.Add("voice_enabled", OSD.FromBoolean(false));
620
621 OSDMap bodyMap = new OSDMap(4);
622 bodyMap.Add("session_id", OSD.FromUUID(groupID));
623 bodyMap.Add("temp_session_id", OSD.FromUUID(groupID));
624 bodyMap.Add("success", OSD.FromBoolean(true));
625 bodyMap.Add("session_info", sessionMap);
626
627 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
628
629 if (queue != null)
630 {
631 queue.Enqueue(queue.BuildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId);
632 }
633 }
634
635 private void DebugGridInstantMessage(GridInstantMessage im)
636 {
637 // Don't log any normal IMs (privacy!)
638 if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent)
639 {
640 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromGroup({0})", im.fromGroup ? "True" : "False");
641 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: Dialog({0})", (InstantMessageDialog)im.dialog);
642 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromAgentID({0})", im.fromAgentID);
643 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromAgentName({0})", im.fromAgentName);
644 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: imSessionID({0})", im.imSessionID);
645 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: message({0})", im.message);
646 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: offline({0})", im.offline);
647 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: toAgentID({0})", im.toAgentID);
648 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket"));
649 }
650 }
651
652 #region Client Tools
653
654 /// <summary>
655 /// Try to find an active IClientAPI reference for agentID giving preference to root connections
656 /// </summary>
657 private IClientAPI GetActiveClient(UUID agentID)
658 {
659 if (m_debugEnabled)
660 m_log.DebugFormat("[GROUPS-MESSAGING]: Looking for local client {0}", agentID);
661
662 IClientAPI child = null;
663
664 // Try root avatar first
665 foreach (Scene scene in m_sceneList)
666 {
667 ScenePresence sp = scene.GetScenePresence(agentID);
668 if (sp != null)
669 {
670 if (!sp.IsChildAgent)
671 {
672 if (m_debugEnabled)
673 m_log.DebugFormat("[GROUPS-MESSAGING]: Found root agent for client : {0}", sp.ControllingClient.Name);
674
675 return sp.ControllingClient;
676 }
677 else
678 {
679 if (m_debugEnabled)
680 m_log.DebugFormat("[GROUPS-MESSAGING]: Found child agent for client : {0}", sp.ControllingClient.Name);
681
682 child = sp.ControllingClient;
683 }
684 }
685 }
686
687 // If we didn't find a root, then just return whichever child we found, or null if none
688 if (child == null)
689 {
690 if (m_debugEnabled)
691 m_log.DebugFormat("[GROUPS-MESSAGING]: Could not find local client for agent : {0}", agentID);
692 }
693 else
694 {
695 if (m_debugEnabled)
696 m_log.DebugFormat("[GROUPS-MESSAGING]: Returning child agent for client : {0}", child.Name);
697 }
698
699 return child;
700 }
701
702 #endregion
703 }
704}
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
new file mode 100644
index 0000000..dff3f78
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
@@ -0,0 +1,1554 @@
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;
32using log4net;
33using Mono.Addins;
34using Nini.Config;
35using OpenMetaverse;
36using OpenMetaverse.StructuredData;
37using OpenSim.Framework;
38using OpenSim.Framework.Communications;
39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes;
41using OpenSim.Services.Interfaces;
42using System.Text;
43using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags;
44
45namespace 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;
81
82 private IGroupsMessagingModule m_groupsMessagingModule;
83
84 private IGroupsServicesConnector m_groupData;
85
86 // Configuration settings
87 private bool m_groupsEnabled = false;
88 private bool m_groupNoticesEnabled = true;
89 private bool m_debugEnabled = false;
90 private int m_levelGroupCreate = 0;
91
92 #region Region Module interfaceBase Members
93
94 public void Initialise(IConfigSource config)
95 {
96 IConfig groupsConfig = config.Configs["Groups"];
97
98 if (groupsConfig == null)
99 {
100 // Do not run this module by default.
101 return;
102 }
103 else
104 {
105 m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false);
106 if (!m_groupsEnabled)
107 {
108 return;
109 }
110
111 if (groupsConfig.GetString("Module", "Default") != Name)
112 {
113 m_groupsEnabled = false;
114
115 return;
116 }
117
118 m_log.InfoFormat("[GROUPS]: Initializing {0}", this.Name);
119
120 m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true);
121 m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false);
122 m_levelGroupCreate = groupsConfig.GetInt("LevelGroupCreate", 0);
123 }
124 }
125
126 public void AddRegion(Scene scene)
127 {
128 if (m_groupsEnabled)
129 {
130 scene.RegisterModuleInterface<IGroupsModule>(this);
131 scene.AddCommand(
132 "Debug",
133 this,
134 "debug groups verbose",
135 "debug groups verbose <true|false>",
136 "This setting turns on very verbose groups debugging",
137 HandleDebugGroupsVerbose);
138 }
139 }
140
141 private void HandleDebugGroupsVerbose(object modules, string[] args)
142 {
143 if (args.Length < 4)
144 {
145 MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
146 return;
147 }
148
149 bool verbose = false;
150 if (!bool.TryParse(args[3], out verbose))
151 {
152 MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
153 return;
154 }
155
156 m_debugEnabled = verbose;
157
158 MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
159 }
160
161 public void RegionLoaded(Scene scene)
162 {
163 if (!m_groupsEnabled)
164 return;
165
166 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
167
168 if (m_groupData == null)
169 {
170 m_groupData = scene.RequestModuleInterface<IGroupsServicesConnector>();
171
172 // No Groups Service Connector, then nothing works...
173 if (m_groupData == null)
174 {
175 m_groupsEnabled = false;
176 m_log.Error("[GROUPS]: Could not get IGroupsServicesConnector");
177 Close();
178 return;
179 }
180 }
181
182 if (m_msgTransferModule == null)
183 {
184 m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
185
186 // No message transfer module, no notices, group invites, rejects, ejects, etc
187 if (m_msgTransferModule == null)
188 {
189 m_groupsEnabled = false;
190 m_log.Warn("[GROUPS]: Could not get IMessageTransferModule");
191 }
192 }
193
194 if (m_groupsMessagingModule == null)
195 {
196 m_groupsMessagingModule = scene.RequestModuleInterface<IGroupsMessagingModule>();
197
198 // No message transfer module, no notices, group invites, rejects, ejects, etc
199 if (m_groupsMessagingModule == null)
200 m_log.Warn("[GROUPS]: Could not get IGroupsMessagingModule");
201 }
202
203 lock (m_sceneList)
204 {
205 m_sceneList.Add(scene);
206 }
207
208 scene.EventManager.OnNewClient += OnNewClient;
209 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
210 // The InstantMessageModule itself doesn't do this,
211 // so lets see if things explode if we don't do it
212 // scene.EventManager.OnClientClosed += OnClientClosed;
213
214 }
215
216 public void RemoveRegion(Scene scene)
217 {
218 if (!m_groupsEnabled)
219 return;
220
221 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
222
223 lock (m_sceneList)
224 {
225 m_sceneList.Remove(scene);
226 }
227 }
228
229 public void Close()
230 {
231 if (!m_groupsEnabled)
232 return;
233
234 if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down Groups module.");
235 }
236
237 public Type ReplaceableInterface
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.OnRequestAvatarProperties += OnRequestAvatarProperties;
266
267 // Used for Notices and Group Invites/Accept/Reject
268 client.OnInstantMessage += OnInstantMessage;
269
270 // Send client their groups information.
271 SendAgentGroupDataUpdate(client, client.AgentId);
272 }
273
274 private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID)
275 {
276 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
277
278 //GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetRequestingAgentID(remoteClient), avatarID).ToArray();
279 GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID);
280 remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups);
281 }
282
283 /*
284 * This becomes very problematic in a shared module. In a shared module you may have more then one
285 * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections.
286 * The OnClientClosed event does not provide anything to indicate which one of those should be closed
287 * nor does it provide what scene it was from so that the specific reference can be looked up.
288 * The InstantMessageModule.cs does not currently worry about unregistering the handles,
289 * and it should be an issue, since it's the client that references us not the other way around
290 * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed
291 private void OnClientClosed(UUID AgentId)
292 {
293 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
294
295 lock (m_ActiveClients)
296 {
297 if (m_ActiveClients.ContainsKey(AgentId))
298 {
299 IClientAPI client = m_ActiveClients[AgentId];
300 client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
301 client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest;
302 client.OnDirFindQuery -= OnDirFindQuery;
303 client.OnInstantMessage -= OnInstantMessage;
304
305 m_ActiveClients.Remove(AgentId);
306 }
307 else
308 {
309 if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Client closed that wasn't registered here.");
310 }
311
312
313 }
314 }
315 */
316
317 private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
318 {
319 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
320
321 UUID activeGroupID = UUID.Zero;
322 string activeGroupTitle = string.Empty;
323 string activeGroupName = string.Empty;
324 ulong activeGroupPowers = (ulong)GroupPowers.None;
325
326 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient), dataForAgentID);
327 if (membership != null)
328 {
329 activeGroupID = membership.GroupID;
330 activeGroupTitle = membership.GroupTitle;
331 activeGroupPowers = membership.GroupPowers;
332 }
333
334 SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle);
335
336 SendScenePresenceUpdate(dataForAgentID, activeGroupTitle);
337 }
338
339 private void HandleUUIDGroupNameRequest(UUID GroupID, IClientAPI remoteClient)
340 {
341 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
342
343 string GroupName;
344
345 GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null);
346 if (group != null)
347 {
348 GroupName = group.GroupName;
349 }
350 else
351 {
352 GroupName = "Unknown";
353 }
354
355 remoteClient.SendGroupNameReply(GroupID, GroupName);
356 }
357
358 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
359 {
360 if (m_debugEnabled)
361 m_log.DebugFormat(
362 "[GROUPS]: {0} called for {1}, message type {2}",
363 System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name, (InstantMessageDialog)im.dialog);
364
365 // Group invitations
366 if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
367 {
368 UUID inviteID = new UUID(im.imSessionID);
369 GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID);
370
371 if (inviteInfo == null)
372 {
373 if (m_debugEnabled) m_log.WarnFormat("[GROUPS]: Received an Invite IM for an invite that does not exist {0}.", inviteID);
374 return;
375 }
376
377 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID);
378
379 UUID fromAgentID = new UUID(im.fromAgentID);
380 if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID))
381 {
382 // Accept
383 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept)
384 {
385 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received an accept invite notice.");
386
387 // and the sessionid is the role
388 m_groupData.AddAgentToGroup(GetRequestingAgentID(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID);
389
390 GridInstantMessage msg = new GridInstantMessage();
391 msg.imSessionID = UUID.Zero.Guid;
392 msg.fromAgentID = UUID.Zero.Guid;
393 msg.toAgentID = inviteInfo.AgentID.Guid;
394 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
395 msg.fromAgentName = "Groups";
396 msg.message = string.Format("You have been added to the group.");
397 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox;
398 msg.fromGroup = false;
399 msg.offline = (byte)0;
400 msg.ParentEstateID = 0;
401 msg.Position = Vector3.Zero;
402 msg.RegionID = UUID.Zero.Guid;
403 msg.binaryBucket = new byte[0];
404
405 OutgoingInstantMessage(msg, inviteInfo.AgentID);
406
407 UpdateAllClientsWithGroupInfo(inviteInfo.AgentID);
408
409 // TODO: If the inviter is still online, they need an agent dataupdate
410 // and maybe group membership updates for the invitee
411
412 m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID);
413 }
414
415 // Reject
416 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)
417 {
418 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Received a reject invite notice.");
419 m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentID(remoteClient), inviteID);
420 }
421 }
422 }
423
424 // Group notices
425 if ((im.dialog == (byte)InstantMessageDialog.GroupNotice))
426 {
427 if (!m_groupNoticesEnabled)
428 {
429 return;
430 }
431
432 UUID GroupID = new UUID(im.toAgentID);
433 if (m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), GroupID, null) != null)
434 {
435 UUID NoticeID = UUID.Random();
436 string Subject = im.message.Substring(0, im.message.IndexOf('|'));
437 string Message = im.message.Substring(Subject.Length + 1);
438
439 InventoryItemBase item = null;
440 bool hasAttachment = false;
441 UUID itemID = UUID.Zero; //Assignment to quiet compiler
442 UUID ownerID = UUID.Zero; //Assignment to quiet compiler
443 byte[] bucket;
444
445 if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0)
446 {
447 string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
448 binBucket = binBucket.Remove(0, 14).Trim();
449
450 OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket);
451 if (binBucketOSD is OSD)
452 {
453 OSDMap binBucketMap = (OSDMap)binBucketOSD;
454
455 itemID = binBucketMap["item_id"].AsUUID();
456 ownerID = binBucketMap["owner_id"].AsUUID();
457
458 //Attempt to get the details of the attached item.
459 //If sender doesn't own the attachment, the item
460 //variable will be set to null and attachment will
461 //not be included with the group notice.
462 Scene scene = (Scene)remoteClient.Scene;
463 item = new InventoryItemBase(itemID, ownerID);
464 item = scene.InventoryService.GetItem(item);
465
466 if (item != null)
467 {
468 //Got item details so include the attachment.
469 hasAttachment = true;
470 }
471 }
472 else
473 {
474 m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType());
475 }
476 }
477
478 if (hasAttachment)
479 {
480 //Bucket contains information about attachment.
481 //
482 //Byte offset and description of bucket data:
483 //0: 1 byte indicating if attachment is present
484 //1: 1 byte indicating the type of attachment
485 //2: 16 bytes - Group UUID
486 //18: 16 bytes - UUID of the attachment owner
487 //34: 16 bytes - UUID of the attachment
488 //50: variable - Name of the attachment
489 //??: NUL byte to terminate the attachment name
490 byte[] name = Encoding.UTF8.GetBytes(item.Name);
491 bucket = new byte[51 + name.Length];//3 bytes, 3 UUIDs, and name
492 bucket[0] = 1; //Has attachment flag
493 bucket[1] = (byte)item.InvType; //Type of Attachment
494 GroupID.ToBytes(bucket, 2);
495 ownerID.ToBytes(bucket, 18);
496 itemID.ToBytes(bucket, 34);
497 name.CopyTo(bucket, 50);
498 }
499 else
500 {
501 bucket = new byte[19];
502 bucket[0] = 0; //Has attachment flag
503 bucket[1] = 0; //Type of attachment
504 GroupID.ToBytes(bucket, 2);
505 bucket[18] = 0; //NUL terminate name of attachment
506 }
507
508 m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket);
509 if (OnNewGroupNotice != null)
510 {
511 OnNewGroupNotice(GroupID, NoticeID);
512 }
513
514 if (m_debugEnabled)
515 {
516 foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID))
517 {
518 if (m_debugEnabled)
519 {
520 UserAccount targetUser
521 = m_sceneList[0].UserAccountService.GetUserAccount(
522 remoteClient.Scene.RegionInfo.ScopeID, member.AgentID);
523
524 if (targetUser != null)
525 {
526 m_log.DebugFormat(
527 "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
528 NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices);
529 }
530 else
531 {
532 m_log.DebugFormat(
533 "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
534 NoticeID, member.AgentID, member.AcceptNotices);
535 }
536 }
537 }
538 }
539
540 GridInstantMessage msg
541 = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
542
543 if (m_groupsMessagingModule != null)
544 m_groupsMessagingModule.SendMessageToGroup(
545 msg, GroupID, remoteClient.AgentId, gmd => gmd.AcceptNotices);
546 }
547 }
548
549 if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted)
550 {
551 //Is bucket large enough to hold UUID of the attachment?
552 if (im.binaryBucket.Length < 16)
553 return;
554
555 UUID noticeID = new UUID(im.imSessionID);
556
557 if (m_debugEnabled)
558 m_log.DebugFormat("[GROUPS]: Requesting notice {0} for {1}", noticeID, remoteClient.AgentId);
559
560 GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID);
561 if (notice != null)
562 {
563 UUID giver = new UUID(notice.BinaryBucket, 18);
564 UUID attachmentUUID = new UUID(notice.BinaryBucket, 34);
565
566 if (m_debugEnabled)
567 m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId);
568
569 string message;
570 InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId,
571 giver, attachmentUUID, out message);
572
573 if (itemCopy == null)
574 {
575 remoteClient.SendAgentAlertMessage(message, false);
576 return;
577 }
578
579 remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0);
580 }
581 else
582 {
583 if (m_debugEnabled)
584 m_log.DebugFormat(
585 "[GROUPS]: Could not find notice {0} for {1} on GroupNoticeInventoryAccepted.",
586 noticeID, remoteClient.AgentId);
587 }
588 }
589
590 // Interop, received special 210 code for ejecting a group member
591 // this only works within the comms servers domain, and won't work hypergrid
592 // TODO:FIXME: Use a presence server of some kind to find out where the
593 // client actually is, and try contacting that region directly to notify them,
594 // or provide the notification via xmlrpc update queue
595 if ((im.dialog == 210))
596 {
597 // This is sent from the region that the ejectee was ejected from
598 // if it's being delivered here, then the ejectee is here
599 // so we need to send local updates to the agent.
600
601 UUID ejecteeID = new UUID(im.toAgentID);
602
603 im.dialog = (byte)InstantMessageDialog.MessageFromAgent;
604 OutgoingInstantMessage(im, ejecteeID);
605
606 IClientAPI ejectee = GetActiveClient(ejecteeID);
607 if (ejectee != null)
608 {
609 UUID groupID = new UUID(im.imSessionID);
610 ejectee.SendAgentDropGroup(groupID);
611 }
612 }
613 }
614
615 private void OnGridInstantMessage(GridInstantMessage msg)
616 {
617 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
618
619 // Trigger the above event handler
620 OnInstantMessage(null, msg);
621
622 // If a message from a group arrives here, it may need to be forwarded to a local client
623 if (msg.fromGroup == true)
624 {
625 switch (msg.dialog)
626 {
627 case (byte)InstantMessageDialog.GroupInvitation:
628 case (byte)InstantMessageDialog.GroupNotice:
629 UUID toAgentID = new UUID(msg.toAgentID);
630 IClientAPI localClient = GetActiveClient(toAgentID);
631 if (localClient != null)
632 {
633 localClient.SendInstantMessage(msg);
634 }
635 break;
636 }
637 }
638 }
639
640 #endregion
641
642 #region IGroupsModule Members
643
644 public event NewGroupNotice OnNewGroupNotice;
645
646 public GroupRecord GetGroupRecord(UUID GroupID)
647 {
648 return m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
649 }
650
651 public GroupRecord GetGroupRecord(string name)
652 {
653 return m_groupData.GetGroupRecord(UUID.Zero, UUID.Zero, name);
654 }
655
656 public void ActivateGroup(IClientAPI remoteClient, UUID groupID)
657 {
658 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
659
660 m_groupData.SetAgentActiveGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
661
662 // Changing active group changes title, active powers, all kinds of things
663 // anyone who is in any region that can see this client, should probably be
664 // updated with new group info. At a minimum, they should get ScenePresence
665 // updated with new title.
666 UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient));
667 }
668
669 /// <summary>
670 /// Get the Role Titles for an Agent, for a specific group
671 /// </summary>
672 public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
673 {
674 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
675
676
677 List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
678 GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
679
680 List<GroupTitlesData> titles = new List<GroupTitlesData>();
681 foreach (GroupRolesData role in agentRoles)
682 {
683 GroupTitlesData title = new GroupTitlesData();
684 title.Name = role.Name;
685 if (agentMembership != null)
686 {
687 title.Selected = agentMembership.ActiveRole == role.RoleID;
688 }
689 title.UUID = role.RoleID;
690
691 titles.Add(title);
692 }
693
694 return titles;
695 }
696
697 public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
698 {
699 if (m_debugEnabled)
700 m_log.DebugFormat(
701 "[GROUPS]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name);
702
703 List<GroupMembersData> data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID);
704
705 if (m_debugEnabled)
706 {
707 foreach (GroupMembersData member in data)
708 {
709 m_log.DebugFormat("[GROUPS]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner);
710 }
711 }
712
713 return data;
714
715 }
716
717 public List<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
718 {
719 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
720
721 List<GroupRolesData> data = m_groupData.GetGroupRoles(GetRequestingAgentID(remoteClient), groupID);
722
723 return data;
724 }
725
726 public List<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
727 {
728 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
729
730 List<GroupRoleMembersData> data = m_groupData.GetGroupRoleMembers(GetRequestingAgentID(remoteClient), groupID);
731
732 if (m_debugEnabled)
733 {
734 foreach (GroupRoleMembersData member in data)
735 {
736 m_log.DebugFormat("[GROUPS]: Member({0}) - Role({1})", member.MemberID, member.RoleID);
737 }
738 }
739 return data;
740 }
741
742 public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID)
743 {
744 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
745
746 GroupProfileData profile = new GroupProfileData();
747
748
749 GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), groupID, null);
750 if (groupInfo != null)
751 {
752 profile.AllowPublish = groupInfo.AllowPublish;
753 profile.Charter = groupInfo.Charter;
754 profile.FounderID = groupInfo.FounderID;
755 profile.GroupID = groupID;
756 profile.GroupMembershipCount = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID).Count;
757 profile.GroupRolesCount = m_groupData.GetGroupRoles(GetRequestingAgentID(remoteClient), groupID).Count;
758 profile.InsigniaID = groupInfo.GroupPicture;
759 profile.MaturePublish = groupInfo.MaturePublish;
760 profile.MembershipFee = groupInfo.MembershipFee;
761 profile.Money = 0; // TODO: Get this from the currency server?
762 profile.Name = groupInfo.GroupName;
763 profile.OpenEnrollment = groupInfo.OpenEnrollment;
764 profile.OwnerRole = groupInfo.OwnerRoleID;
765 profile.ShowInList = groupInfo.ShowInList;
766 }
767
768 GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
769 if (memberInfo != null)
770 {
771 profile.MemberTitle = memberInfo.GroupTitle;
772 profile.PowersMask = memberInfo.GroupPowers;
773 }
774
775 return profile;
776 }
777
778 public GroupMembershipData[] GetMembershipData(UUID agentID)
779 {
780 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
781
782 return m_groupData.GetAgentGroupMemberships(UUID.Zero, agentID).ToArray();
783 }
784
785 public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
786 {
787 if (m_debugEnabled)
788 m_log.DebugFormat(
789 "[GROUPS]: {0} called with groupID={1}, agentID={2}",
790 System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID);
791
792 return m_groupData.GetAgentGroupMembership(UUID.Zero, agentID, groupID);
793 }
794
795 public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
796 {
797 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
798
799 // Note: Permissions checking for modification rights is handled by the Groups Server/Service
800 m_groupData.UpdateGroup(GetRequestingAgentID(remoteClient), groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
801 }
802
803 public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile)
804 {
805 // Note: Permissions checking for modification rights is handled by the Groups Server/Service
806 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
807
808 m_groupData.SetAgentGroupInfo(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID, acceptNotices, listInProfile);
809 }
810
811 public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
812 {
813 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
814
815 if (m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), UUID.Zero, name) != null)
816 {
817 remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists.");
818 return UUID.Zero;
819 }
820
821 // check user level
822 ScenePresence avatar = null;
823 Scene scene = (Scene)remoteClient.Scene;
824 scene.TryGetScenePresence(remoteClient.AgentId, out avatar);
825
826 if (avatar != null)
827 {
828 if (avatar.UserLevel < m_levelGroupCreate)
829 {
830 remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got insufficient permissions to create a group.");
831 return UUID.Zero;
832 }
833 }
834
835 // check funds
836 // is there is a money module present ?
837 IMoneyModule money = scene.RequestModuleInterface<IMoneyModule>();
838 if (money != null)
839 {
840 // do the transaction, that is if the agent has got sufficient funds
841 if (!money.AmountCovered(remoteClient.AgentId, money.GroupCreationCharge)) {
842 remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got insufficient funds to create a group.");
843 return UUID.Zero;
844 }
845 money.ApplyCharge(GetRequestingAgentID(remoteClient), money.GroupCreationCharge, MoneyTransactionType.GroupCreate);
846 }
847 UUID groupID = m_groupData.CreateGroup(GetRequestingAgentID(remoteClient), name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, GetRequestingAgentID(remoteClient));
848
849 remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly");
850
851 // Update the founder with new group information.
852 SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
853
854 return groupID;
855 }
856
857 public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID)
858 {
859 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
860
861 // ToDo: check if agent is a member of group and is allowed to see notices?
862
863 return m_groupData.GetGroupNotices(GetRequestingAgentID(remoteClient), groupID).ToArray();
864 }
865
866 /// <summary>
867 /// Get the title of the agent's current role.
868 /// </summary>
869 public string GetGroupTitle(UUID avatarID)
870 {
871 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
872
873 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(UUID.Zero, avatarID);
874 if (membership != null)
875 {
876 return membership.GroupTitle;
877 }
878 return string.Empty;
879 }
880
881 /// <summary>
882 /// Change the current Active Group Role for Agent
883 /// </summary>
884 public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID)
885 {
886 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
887
888 m_groupData.SetAgentActiveGroupRole(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID, titleRoleID);
889
890 // TODO: Not sure what all is needed here, but if the active group role change is for the group
891 // the client currently has set active, then we need to do a scene presence update too
892 // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID)
893
894 UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient));
895 }
896
897
898 public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType)
899 {
900 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
901
902 // Security Checks are handled in the Groups Service.
903
904 switch ((OpenMetaverse.GroupRoleUpdate)updateType)
905 {
906 case OpenMetaverse.GroupRoleUpdate.Create:
907 m_groupData.AddGroupRole(GetRequestingAgentID(remoteClient), groupID, UUID.Random(), name, description, title, powers);
908 break;
909
910 case OpenMetaverse.GroupRoleUpdate.Delete:
911 m_groupData.RemoveGroupRole(GetRequestingAgentID(remoteClient), groupID, roleID);
912 break;
913
914 case OpenMetaverse.GroupRoleUpdate.UpdateAll:
915 case OpenMetaverse.GroupRoleUpdate.UpdateData:
916 case OpenMetaverse.GroupRoleUpdate.UpdatePowers:
917 if (m_debugEnabled)
918 {
919 GroupPowers gp = (GroupPowers)powers;
920 m_log.DebugFormat("[GROUPS]: Role ({0}) updated with Powers ({1}) ({2})", name, powers.ToString(), gp.ToString());
921 }
922 m_groupData.UpdateGroupRole(GetRequestingAgentID(remoteClient), groupID, roleID, name, description, title, powers);
923 break;
924
925 case OpenMetaverse.GroupRoleUpdate.NoUpdate:
926 default:
927 // No Op
928 break;
929
930 }
931
932 // TODO: This update really should send out updates for everyone in the role that just got changed.
933 SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
934 }
935
936 public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes)
937 {
938 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
939 // Todo: Security check
940
941 switch (changes)
942 {
943 case 0:
944 // Add
945 m_groupData.AddAgentToGroupRole(GetRequestingAgentID(remoteClient), memberID, groupID, roleID);
946
947 break;
948 case 1:
949 // Remove
950 m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentID(remoteClient), memberID, groupID, roleID);
951
952 break;
953 default:
954 m_log.ErrorFormat("[GROUPS]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes);
955 break;
956 }
957
958 // TODO: This update really should send out updates for everyone in the role that just got changed.
959 SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
960 }
961
962 public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID)
963 {
964 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
965
966 GroupNoticeInfo data = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), groupNoticeID);
967
968 if (data != null)
969 {
970 GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested);
971
972 OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient));
973 }
974 }
975
976 public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
977 {
978 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
979
980 GridInstantMessage msg = new GridInstantMessage();
981 byte[] bucket;
982
983 msg.imSessionID = groupNoticeID.Guid;
984 msg.toAgentID = agentID.Guid;
985 msg.dialog = dialog;
986 msg.fromGroup = true;
987 msg.offline = (byte)0;
988 msg.ParentEstateID = 0;
989 msg.Position = Vector3.Zero;
990 msg.RegionID = UUID.Zero.Guid;
991
992 GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID, groupNoticeID);
993 if (info != null)
994 {
995 msg.fromAgentID = info.GroupID.Guid;
996 msg.timestamp = info.noticeData.Timestamp;
997 msg.fromAgentName = info.noticeData.FromName;
998 msg.message = info.noticeData.Subject + "|" + info.Message;
999
1000 if (info.BinaryBucket[0] > 0)
1001 {
1002 //32 is due to not needing space for two of the UUIDs.
1003 //(Don't need UUID of attachment or its owner in IM)
1004 //50 offset gets us to start of attachment name.
1005 //We are skipping the attachment flag, type, and
1006 //the three UUID fields at the start of the bucket.
1007 bucket = new byte[info.BinaryBucket.Length-32];
1008 bucket[0] = 1; //Has attachment
1009 bucket[1] = info.BinaryBucket[1];
1010 Array.Copy(info.BinaryBucket, 50,
1011 bucket, 18, info.BinaryBucket.Length-50);
1012 }
1013 else
1014 {
1015 bucket = new byte[19];
1016 bucket[0] = 0; //No attachment
1017 bucket[1] = 0; //Attachment type
1018 bucket[18] = 0; //NUL terminate name
1019 }
1020
1021 info.GroupID.ToBytes(bucket, 2);
1022 msg.binaryBucket = bucket;
1023 }
1024 else
1025 {
1026 if (m_debugEnabled)
1027 m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID);
1028
1029 msg.fromAgentID = UUID.Zero.Guid;
1030 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1031 msg.fromAgentName = string.Empty;
1032 msg.message = string.Empty;
1033 msg.binaryBucket = new byte[0];
1034 }
1035
1036 return msg;
1037 }
1038
1039 public void SendAgentGroupDataUpdate(IClientAPI remoteClient)
1040 {
1041 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1042
1043 // Send agent information about his groups
1044 SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
1045 }
1046
1047 public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID)
1048 {
1049 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1050
1051 // Should check to see if OpenEnrollment, or if there's an outstanding invitation
1052 m_groupData.AddAgentToGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID, UUID.Zero);
1053
1054 remoteClient.SendJoinGroupReply(groupID, true);
1055
1056 // Should this send updates to everyone in the group?
1057 SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
1058 }
1059
1060 public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID)
1061 {
1062 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1063
1064 m_groupData.RemoveAgentFromGroup(GetRequestingAgentID(remoteClient), GetRequestingAgentID(remoteClient), groupID);
1065
1066 remoteClient.SendLeaveGroupReply(groupID, true);
1067
1068 remoteClient.SendAgentDropGroup(groupID);
1069
1070 // SL sends out notifcations to the group messaging session that the person has left
1071 // Should this also update everyone who is in the group?
1072 SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
1073 }
1074
1075 public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID)
1076 {
1077 EjectGroupMember(remoteClient, GetRequestingAgentID(remoteClient), groupID, ejecteeID);
1078 }
1079
1080 public void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID)
1081 {
1082 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1083
1084 // Todo: Security check?
1085 m_groupData.RemoveAgentFromGroup(agentID, ejecteeID, groupID);
1086
1087 string agentName;
1088 RegionInfo regionInfo;
1089
1090 // remoteClient provided or just agentID?
1091 if (remoteClient != null)
1092 {
1093 agentName = remoteClient.Name;
1094 regionInfo = remoteClient.Scene.RegionInfo;
1095 remoteClient.SendEjectGroupMemberReply(agentID, groupID, true);
1096 }
1097 else
1098 {
1099 IClientAPI client = GetActiveClient(agentID);
1100
1101 if (client != null)
1102 {
1103 agentName = client.Name;
1104 regionInfo = client.Scene.RegionInfo;
1105 client.SendEjectGroupMemberReply(agentID, groupID, true);
1106 }
1107 else
1108 {
1109 regionInfo = m_sceneList[0].RegionInfo;
1110 UserAccount acc = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID);
1111
1112 if (acc != null)
1113 {
1114 agentName = acc.FirstName + " " + acc.LastName;
1115 }
1116 else
1117 {
1118 agentName = "Unknown member";
1119 }
1120 }
1121 }
1122
1123 GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID, groupID, null);
1124
1125 UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, ejecteeID);
1126 if ((groupInfo == null) || (account == null))
1127 {
1128 return;
1129 }
1130
1131 // Send Message to Ejectee
1132 GridInstantMessage msg = new GridInstantMessage();
1133
1134 msg.imSessionID = UUID.Zero.Guid;
1135 msg.fromAgentID = agentID.Guid;
1136 // msg.fromAgentID = info.GroupID;
1137 msg.toAgentID = ejecteeID.Guid;
1138 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1139 msg.timestamp = 0;
1140 msg.fromAgentName = agentName;
1141 msg.message = string.Format("You have been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName);
1142 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent;
1143 msg.fromGroup = false;
1144 msg.offline = (byte)0;
1145 msg.ParentEstateID = 0;
1146 msg.Position = Vector3.Zero;
1147 msg.RegionID = regionInfo.RegionID.Guid;
1148 msg.binaryBucket = new byte[0];
1149 OutgoingInstantMessage(msg, ejecteeID);
1150
1151 // Message to ejector
1152 // Interop, received special 210 code for ejecting a group member
1153 // this only works within the comms servers domain, and won't work hypergrid
1154 // TODO:FIXME: Use a presence server of some kind to find out where the
1155 // client actually is, and try contacting that region directly to notify them,
1156 // or provide the notification via xmlrpc update queue
1157
1158 msg = new GridInstantMessage();
1159 msg.imSessionID = UUID.Zero.Guid;
1160 msg.fromAgentID = agentID.Guid;
1161 msg.toAgentID = agentID.Guid;
1162 msg.timestamp = 0;
1163 msg.fromAgentName = agentName;
1164 if (account != null)
1165 {
1166 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, account.FirstName + " " + account.LastName);
1167 }
1168 else
1169 {
1170 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, "Unknown member");
1171 }
1172 msg.dialog = (byte)210; //interop
1173 msg.fromGroup = false;
1174 msg.offline = (byte)0;
1175 msg.ParentEstateID = 0;
1176 msg.Position = Vector3.Zero;
1177 msg.RegionID = regionInfo.RegionID.Guid;
1178 msg.binaryBucket = new byte[0];
1179 OutgoingInstantMessage(msg, agentID);
1180
1181
1182 // SL sends out messages to everyone in the group
1183 // Who all should receive updates and what should they be updated with?
1184 UpdateAllClientsWithGroupInfo(ejecteeID);
1185 }
1186
1187 public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID)
1188 {
1189 InviteGroup(remoteClient, GetRequestingAgentID(remoteClient), groupID, invitedAgentID, roleID);
1190 }
1191
1192 public void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID)
1193 {
1194 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1195
1196 string agentName;
1197 RegionInfo regionInfo;
1198
1199 // remoteClient provided or just agentID?
1200 if (remoteClient != null)
1201 {
1202 agentName = remoteClient.Name;
1203 regionInfo = remoteClient.Scene.RegionInfo;
1204 }
1205 else
1206 {
1207 IClientAPI client = GetActiveClient(agentID);
1208
1209 if (client != null)
1210 {
1211 agentName = client.Name;
1212 regionInfo = client.Scene.RegionInfo;
1213 }
1214 else
1215 {
1216 regionInfo = m_sceneList[0].RegionInfo;
1217 UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID);
1218
1219 if (account != null)
1220 {
1221 agentName = account.FirstName + " " + account.LastName;
1222 }
1223 else
1224 {
1225 agentName = "Unknown member";
1226 }
1227 }
1228 }
1229
1230 // Todo: Security check, probably also want to send some kind of notification
1231 UUID InviteID = UUID.Random();
1232
1233 m_groupData.AddAgentToGroupInvite(agentID, InviteID, groupID, roleID, invitedAgentID);
1234
1235 // Check to see if the invite went through, if it did not then it's possible
1236 // the remoteClient did not validate or did not have permission to invite.
1237 GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(agentID, InviteID);
1238
1239 if (inviteInfo != null)
1240 {
1241 if (m_msgTransferModule != null)
1242 {
1243 Guid inviteUUID = InviteID.Guid;
1244
1245 GridInstantMessage msg = new GridInstantMessage();
1246
1247 msg.imSessionID = inviteUUID;
1248
1249 // msg.fromAgentID = agentID.Guid;
1250 msg.fromAgentID = groupID.Guid;
1251 msg.toAgentID = invitedAgentID.Guid;
1252 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1253 msg.timestamp = 0;
1254 msg.fromAgentName = agentName;
1255 msg.message = string.Format("{0} has invited you to join a group. There is no cost to join this group.", agentName);
1256 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
1257 msg.fromGroup = true;
1258 msg.offline = (byte)0;
1259 msg.ParentEstateID = 0;
1260 msg.Position = Vector3.Zero;
1261 msg.RegionID = regionInfo.RegionID.Guid;
1262 msg.binaryBucket = new byte[20];
1263
1264 OutgoingInstantMessage(msg, invitedAgentID);
1265 }
1266 }
1267 }
1268
1269 public List<DirGroupsReplyData> FindGroups(IClientAPI remoteClient, string query)
1270 {
1271 return m_groupData.FindGroups(GetRequestingAgentID(remoteClient), query);
1272 }
1273
1274
1275 #endregion
1276
1277 #region Client/Update Tools
1278
1279 /// <summary>
1280 /// Try to find an active IClientAPI reference for agentID giving preference to root connections
1281 /// </summary>
1282 private IClientAPI GetActiveClient(UUID agentID)
1283 {
1284 IClientAPI child = null;
1285
1286 // Try root avatar first
1287 foreach (Scene scene in m_sceneList)
1288 {
1289 ScenePresence sp = scene.GetScenePresence(agentID);
1290 if (sp != null)
1291 {
1292 if (!sp.IsChildAgent)
1293 {
1294 return sp.ControllingClient;
1295 }
1296 else
1297 {
1298 child = sp.ControllingClient;
1299 }
1300 }
1301 }
1302
1303 // If we didn't find a root, then just return whichever child we found, or null if none
1304 return child;
1305 }
1306
1307 /// <summary>
1308 /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'.
1309 /// </summary>
1310 private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data)
1311 {
1312 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1313
1314 OSDArray AgentData = new OSDArray(1);
1315 OSDMap AgentDataMap = new OSDMap(1);
1316 AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID));
1317 AgentData.Add(AgentDataMap);
1318
1319 OSDArray GroupData = new OSDArray(data.Length);
1320 OSDArray NewGroupData = new OSDArray(data.Length);
1321
1322 foreach (GroupMembershipData membership in data)
1323 {
1324 if (GetRequestingAgentID(remoteClient) != dataForAgentID)
1325 {
1326 if (!membership.ListInProfile)
1327 {
1328 // If we're sending group info to remoteclient about another agent,
1329 // filter out groups the other agent doesn't want to share.
1330 continue;
1331 }
1332 }
1333
1334 OSDMap GroupDataMap = new OSDMap(6);
1335 OSDMap NewGroupDataMap = new OSDMap(1);
1336
1337 GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID));
1338 GroupDataMap.Add("GroupPowers", OSD.FromULong(membership.GroupPowers));
1339 GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices));
1340 GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture));
1341 GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution));
1342 GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName));
1343 NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile));
1344
1345 GroupData.Add(GroupDataMap);
1346 NewGroupData.Add(NewGroupDataMap);
1347 }
1348
1349 OSDMap llDataStruct = new OSDMap(3);
1350 llDataStruct.Add("AgentData", AgentData);
1351 llDataStruct.Add("GroupData", GroupData);
1352 llDataStruct.Add("NewGroupData", NewGroupData);
1353
1354 if (m_debugEnabled)
1355 {
1356 m_log.InfoFormat("[GROUPS]: {0}", OSDParser.SerializeJsonString(llDataStruct));
1357 }
1358
1359 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
1360
1361 if (queue != null)
1362 {
1363 queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient));
1364 }
1365
1366 }
1367
1368 private void SendScenePresenceUpdate(UUID AgentID, string Title)
1369 {
1370 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Updating scene title for {0} with title: {1}", AgentID, Title);
1371
1372 ScenePresence presence = null;
1373
1374 foreach (Scene scene in m_sceneList)
1375 {
1376 presence = scene.GetScenePresence(AgentID);
1377 if (presence != null)
1378 {
1379 if (presence.Grouptitle != Title)
1380 {
1381 presence.Grouptitle = Title;
1382
1383 if (! presence.IsChildAgent)
1384 presence.SendAvatarDataToAllClients();
1385 }
1386 }
1387 }
1388 }
1389
1390 /// <summary>
1391 /// Send updates to all clients who might be interested in groups data for dataForClientID
1392 /// </summary>
1393 private void UpdateAllClientsWithGroupInfo(UUID dataForClientID)
1394 {
1395 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1396
1397 // TODO: Probably isn't nessesary to update every client in every scene.
1398 // Need to examine client updates and do only what's nessesary.
1399 lock (m_sceneList)
1400 {
1401 foreach (Scene scene in m_sceneList)
1402 {
1403 scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); });
1404 }
1405 }
1406 }
1407
1408 /// <summary>
1409 /// Update remoteClient with group information about dataForAgentID
1410 /// </summary>
1411 private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID)
1412 {
1413 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name);
1414
1415 // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
1416
1417 OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero);
1418
1419 // Need to send a group membership update to the client
1420 // UDP version doesn't seem to behave nicely. But we're going to send it out here
1421 // with an empty group membership to hopefully remove groups being displayed due
1422 // to the core Groups Stub
1423 remoteClient.SendGroupMembership(new GroupMembershipData[0]);
1424
1425 GroupMembershipData[] membershipArray = GetProfileListedGroupMemberships(remoteClient, dataForAgentID);
1426 SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipArray);
1427 remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipArray);
1428
1429 if (remoteClient.AgentId == dataForAgentID)
1430 remoteClient.RefreshGroupMembership();
1431 }
1432
1433 /// <summary>
1434 /// Get a list of groups memberships for the agent that are marked "ListInProfile"
1435 /// (unless that agent has a godLike aspect, in which case get all groups)
1436 /// </summary>
1437 /// <param name="dataForAgentID"></param>
1438 /// <returns></returns>
1439 private GroupMembershipData[] GetProfileListedGroupMemberships(IClientAPI requestingClient, UUID dataForAgentID)
1440 {
1441 List<GroupMembershipData> membershipData = m_groupData.GetAgentGroupMemberships(requestingClient.AgentId, dataForAgentID);
1442 GroupMembershipData[] membershipArray;
1443
1444 // cScene and property accessor 'isGod' are in support of the opertions to bypass 'hidden' group attributes for
1445 // those with a GodLike aspect.
1446 Scene cScene = (Scene)requestingClient.Scene;
1447 bool isGod = cScene.Permissions.IsGod(requestingClient.AgentId);
1448
1449 if (isGod)
1450 {
1451 membershipArray = membershipData.ToArray();
1452 }
1453 else
1454 {
1455 if (requestingClient.AgentId != dataForAgentID)
1456 {
1457 Predicate<GroupMembershipData> showInProfile = delegate(GroupMembershipData membership)
1458 {
1459 return membership.ListInProfile;
1460 };
1461
1462 membershipArray = membershipData.FindAll(showInProfile).ToArray();
1463 }
1464 else
1465 {
1466 membershipArray = membershipData.ToArray();
1467 }
1468 }
1469
1470 if (m_debugEnabled)
1471 {
1472 m_log.InfoFormat("[GROUPS]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId);
1473 foreach (GroupMembershipData membership in membershipArray)
1474 {
1475 m_log.InfoFormat("[GROUPS]: {0} :: {1} - {2} - {3}", dataForAgentID, membership.GroupName, membership.GroupTitle, membership.GroupPowers);
1476 }
1477 }
1478
1479 return membershipArray;
1480 }
1481
1482
1483 private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle)
1484 {
1485 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1486
1487 // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
1488 UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, dataForAgentID);
1489 string firstname, lastname;
1490 if (account != null)
1491 {
1492 firstname = account.FirstName;
1493 lastname = account.LastName;
1494 }
1495 else
1496 {
1497 firstname = "Unknown";
1498 lastname = "Unknown";
1499 }
1500
1501 remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname,
1502 lastname, activeGroupPowers, activeGroupName,
1503 activeGroupTitle);
1504 }
1505
1506 #endregion
1507
1508 #region IM Backed Processes
1509
1510 private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo)
1511 {
1512 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1513
1514 IClientAPI localClient = GetActiveClient(msgTo);
1515 if (localClient != null)
1516 {
1517 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is local, delivering directly", localClient.Name);
1518 localClient.SendInstantMessage(msg);
1519 }
1520 else if (m_msgTransferModule != null)
1521 {
1522 if (m_debugEnabled) m_log.InfoFormat("[GROUPS]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo);
1523 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Message Sent: {0}", success?"Succeeded":"Failed"); });
1524 }
1525 }
1526
1527 public void NotifyChange(UUID groupID)
1528 {
1529 // Notify all group members of a chnge in group roles and/or
1530 // permissions
1531 //
1532 }
1533
1534 #endregion
1535
1536 private UUID GetRequestingAgentID(IClientAPI client)
1537 {
1538 UUID requestingAgentID = UUID.Zero;
1539 if (client != null)
1540 {
1541 requestingAgentID = client.AgentId;
1542 }
1543 return requestingAgentID;
1544 }
1545 }
1546
1547 public class GroupNoticeInfo
1548 {
1549 public GroupNoticeData noticeData = new GroupNoticeData();
1550 public UUID GroupID = UUID.Zero;
1551 public string Message = string.Empty;
1552 public byte[] BinaryBucket = new byte[0];
1553 }
1554}
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs
new file mode 100644
index 0000000..6b5b40a
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs
@@ -0,0 +1,121 @@
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 OpenMetaverse;
31using OpenSim.Framework;
32
33namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
34{
35 public interface IGroupsServicesConnector
36 {
37 UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID);
38 void UpdateGroup(UUID RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish);
39
40 /// <summary>
41 /// Get the group record.
42 /// </summary>
43 /// <returns></returns>
44 /// <param name='RequestingAgentID'>The UUID of the user making the request.</param>
45 /// <param name='GroupID'>
46 /// The ID of the record to retrieve.
47 /// GroupName may be specified instead, in which case this parameter will be UUID.Zero
48 /// </param>
49 /// <param name='GroupName'>
50 /// The name of the group to retrieve.
51 /// GroupID may be specified instead, in which case this parmeter will be null.
52 /// </param>
53 GroupRecord GetGroupRecord(UUID RequestingAgentID, UUID GroupID, string GroupName);
54
55 List<DirGroupsReplyData> FindGroups(UUID RequestingAgentID, string search);
56 List<GroupMembersData> GetGroupMembers(UUID RequestingAgentID, UUID GroupID);
57
58 void AddGroupRole(UUID RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers);
59 void UpdateGroupRole(UUID RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers);
60 void RemoveGroupRole(UUID RequestingAgentID, UUID groupID, UUID roleID);
61 List<GroupRolesData> GetGroupRoles(UUID RequestingAgentID, UUID GroupID);
62 List<GroupRoleMembersData> GetGroupRoleMembers(UUID RequestingAgentID, UUID GroupID);
63
64 void AddAgentToGroup(UUID RequestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID);
65 void RemoveAgentFromGroup(UUID RequestingAgentID, UUID AgentID, UUID GroupID);
66
67 void AddAgentToGroupInvite(UUID RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID);
68 GroupInviteInfo GetAgentToGroupInvite(UUID RequestingAgentID, UUID inviteID);
69 void RemoveAgentToGroupInvite(UUID RequestingAgentID, UUID inviteID);
70
71 void AddAgentToGroupRole(UUID RequestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID);
72 void RemoveAgentFromGroupRole(UUID RequestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID);
73 List<GroupRolesData> GetAgentGroupRoles(UUID RequestingAgentID, UUID AgentID, UUID GroupID);
74
75 void SetAgentActiveGroup(UUID RequestingAgentID, UUID AgentID, UUID GroupID);
76 GroupMembershipData GetAgentActiveMembership(UUID RequestingAgentID, UUID AgentID);
77
78 void SetAgentActiveGroupRole(UUID RequestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID);
79 void SetAgentGroupInfo(UUID RequestingAgentID, UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile);
80
81 /// <summary>
82 /// Get information about a specific group to which the user belongs.
83 /// </summary>
84 /// <param name="RequestingAgentID">The agent requesting the information.</param>
85 /// <param name="AgentID">The agent requested.</param>
86 /// <param name="GroupID">The group requested.</param>
87 /// <returns>
88 /// If the user is a member of the group then the data structure is returned. If not, then null is returned.
89 /// </returns>
90 GroupMembershipData GetAgentGroupMembership(UUID RequestingAgentID, UUID AgentID, UUID GroupID);
91
92 /// <summary>
93 /// Get information about the groups to which a user belongs.
94 /// </summary>
95 /// <param name="RequestingAgentID">The agent requesting the information.</param>
96 /// <param name="AgentID">The agent requested.</param>
97 /// <returns>
98 /// Information about the groups to which the user belongs. If the user belongs to no groups then an empty
99 /// list is returned.
100 /// </returns>
101 List<GroupMembershipData> GetAgentGroupMemberships(UUID RequestingAgentID, UUID AgentID);
102
103 void AddGroupNotice(UUID RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket);
104 GroupNoticeInfo GetGroupNotice(UUID RequestingAgentID, UUID noticeID);
105 List<GroupNoticeData> GetGroupNotices(UUID RequestingAgentID, UUID GroupID);
106
107 void ResetAgentGroupChatSessions(UUID agentID);
108 bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID);
109 bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID);
110 void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID);
111 void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID);
112 }
113
114 public class GroupInviteInfo
115 {
116 public UUID GroupID = UUID.Zero;
117 public UUID RoleID = UUID.Zero;
118 public UUID AgentID = UUID.Zero;
119 public UUID InviteID = UUID.Zero;
120 }
121}
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
new file mode 100644
index 0000000..8095b28
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
@@ -0,0 +1,1441 @@
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;
30using System.Collections.Generic;
31using System.Collections.Specialized;
32using System.Reflection;
33using System.Threading;
34
35using Nwc.XmlRpc;
36
37using log4net;
38using Mono.Addins;
39using Nini.Config;
40
41using OpenMetaverse;
42using OpenMetaverse.StructuredData;
43
44using OpenSim.Framework;
45using OpenSim.Framework.Communications;
46using OpenSim.Region.Framework.Interfaces;
47using OpenSim.Services.Interfaces;
48
49/***************************************************************************
50 * Simian Data Map
51 * ===============
52 *
53 * OwnerID -> Type -> Key
54 * -----------------------
55 *
56 * UserID -> Group -> ActiveGroup
57 * + GroupID
58 *
59 * UserID -> GroupSessionDropped -> GroupID
60 * UserID -> GroupSessionInvited -> GroupID
61 *
62 * UserID -> GroupMember -> GroupID
63 * + SelectedRoleID [UUID]
64 * + AcceptNotices [bool]
65 * + ListInProfile [bool]
66 * + Contribution [int]
67 *
68 * UserID -> GroupRole[GroupID] -> RoleID
69 *
70 *
71 * GroupID -> Group -> GroupName
72 * + Charter
73 * + ShowInList
74 * + InsigniaID
75 * + MembershipFee
76 * + OpenEnrollment
77 * + AllowPublish
78 * + MaturePublish
79 * + FounderID
80 * + EveryonePowers
81 * + OwnerRoleID
82 * + OwnersPowers
83 *
84 * GroupID -> GroupRole -> RoleID
85 * + Name
86 * + Description
87 * + Title
88 * + Powers
89 *
90 * GroupID -> GroupMemberInvite -> InviteID
91 * + AgentID
92 * + RoleID
93 *
94 * GroupID -> GroupNotice -> NoticeID
95 * + TimeStamp [uint]
96 * + FromName [string]
97 * + Subject [string]
98 * + Message [string]
99 * + BinaryBucket [byte[]]
100 *
101 * */
102
103namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
104{
105 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianGroupsServicesConnectorModule")]
106 public class SimianGroupsServicesConnectorModule : ISharedRegionModule, IGroupsServicesConnector
107 {
108 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
109
110 public const GroupPowers m_DefaultEveryonePowers = GroupPowers.AllowSetHome |
111 GroupPowers.Accountable |
112 GroupPowers.JoinChat |
113 GroupPowers.AllowVoiceChat |
114 GroupPowers.ReceiveNotices |
115 GroupPowers.StartProposal |
116 GroupPowers.VoteOnProposal;
117
118 // Would this be cleaner as (GroupPowers)ulong.MaxValue;
119 public const GroupPowers m_DefaultOwnerPowers = GroupPowers.Accountable
120 | GroupPowers.AllowEditLand
121 | GroupPowers.AllowFly
122 | GroupPowers.AllowLandmark
123 | GroupPowers.AllowRez
124 | GroupPowers.AllowSetHome
125 | GroupPowers.AllowVoiceChat
126 | GroupPowers.AssignMember
127 | GroupPowers.AssignMemberLimited
128 | GroupPowers.ChangeActions
129 | GroupPowers.ChangeIdentity
130 | GroupPowers.ChangeMedia
131 | GroupPowers.ChangeOptions
132 | GroupPowers.CreateRole
133 | GroupPowers.DeedObject
134 | GroupPowers.DeleteRole
135 | GroupPowers.Eject
136 | GroupPowers.FindPlaces
137 | GroupPowers.Invite
138 | GroupPowers.JoinChat
139 | GroupPowers.LandChangeIdentity
140 | GroupPowers.LandDeed
141 | GroupPowers.LandDivideJoin
142 | GroupPowers.LandEdit
143 | GroupPowers.LandEjectAndFreeze
144 | GroupPowers.LandGardening
145 | GroupPowers.LandManageAllowed
146 | GroupPowers.LandManageBanned
147 | GroupPowers.LandManagePasses
148 | GroupPowers.LandOptions
149 | GroupPowers.LandRelease
150 | GroupPowers.LandSetSale
151 | GroupPowers.ModerateChat
152 | GroupPowers.ObjectManipulate
153 | GroupPowers.ObjectSetForSale
154 | GroupPowers.ReceiveNotices
155 | GroupPowers.RemoveMember
156 | GroupPowers.ReturnGroupOwned
157 | GroupPowers.ReturnGroupSet
158 | GroupPowers.ReturnNonGroup
159 | GroupPowers.RoleProperties
160 | GroupPowers.SendNotices
161 | GroupPowers.SetLandingPoint
162 | GroupPowers.StartProposal
163 | GroupPowers.VoteOnProposal;
164
165 private bool m_connectorEnabled = false;
166
167 private string m_groupsServerURI = string.Empty;
168
169 private bool m_debugEnabled = false;
170
171 private Dictionary<string, bool> m_pendingRequests = new Dictionary<string,bool>();
172
173 private ExpiringCache<string, OSDMap> m_memoryCache;
174 private int m_cacheTimeout = 30;
175
176 // private IUserAccountService m_accountService = null;
177
178
179 #region Region Module interfaceBase Members
180
181 public string Name
182 {
183 get { return "SimianGroupsServicesConnector"; }
184 }
185
186 // this module is not intended to be replaced, but there should only be 1 of them.
187 public Type ReplaceableInterface
188 {
189 get { return null; }
190 }
191
192 public void Initialise(IConfigSource config)
193 {
194 IConfig groupsConfig = config.Configs["Groups"];
195
196 if (groupsConfig == null)
197 {
198 // Do not run this module by default.
199 return;
200 }
201 else
202 {
203 // if groups aren't enabled, we're not needed.
204 // if we're not specified as the connector to use, then we're not wanted
205 if ((groupsConfig.GetBoolean("Enabled", false) == false)
206 || (groupsConfig.GetString("ServicesConnectorModule", "XmlRpcGroupsServicesConnector") != Name))
207 {
208 m_connectorEnabled = false;
209 return;
210 }
211
212 m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name);
213
214 m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty);
215 if (string.IsNullOrEmpty(m_groupsServerURI))
216 {
217 m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]");
218 m_connectorEnabled = false;
219 return;
220 }
221
222
223 m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30);
224 if (m_cacheTimeout == 0)
225 {
226 m_log.WarnFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Disabled.");
227 }
228 else
229 {
230 m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Timeout set to {0}.", m_cacheTimeout);
231 }
232
233
234
235 m_memoryCache = new ExpiringCache<string,OSDMap>();
236
237
238 // If we got all the config options we need, lets start'er'up
239 m_connectorEnabled = true;
240
241 m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true);
242
243 }
244 }
245
246 public void Close()
247 {
248 m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Closing {0}", this.Name);
249 }
250
251 public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene)
252 {
253 if (m_connectorEnabled)
254 {
255 scene.RegisterModuleInterface<IGroupsServicesConnector>(this);
256 }
257 }
258
259 public void RemoveRegion(OpenSim.Region.Framework.Scenes.Scene scene)
260 {
261 if (scene.RequestModuleInterface<IGroupsServicesConnector>() == this)
262 {
263 scene.UnregisterModuleInterface<IGroupsServicesConnector>(this);
264 }
265 }
266
267 public void RegionLoaded(OpenSim.Region.Framework.Scenes.Scene scene)
268 {
269 // TODO: May want to consider listenning for Agent Connections so we can pre-cache group info
270 // scene.EventManager.OnNewClient += OnNewClient;
271 }
272
273 #endregion
274
275 #region ISharedRegionModule Members
276
277 public void PostInitialise()
278 {
279 // NoOp
280 }
281
282 #endregion
283
284
285
286
287 #region IGroupsServicesConnector Members
288
289 /// <summary>
290 /// Create a Group, including Everyone and Owners Role, place FounderID in both groups, select Owner as selected role, and newly created group as agent's active role.
291 /// </summary>
292 public UUID CreateGroup(UUID requestingAgentID, string name, string charter, bool showInList, UUID insigniaID,
293 int membershipFee, bool openEnrollment, bool allowPublish,
294 bool maturePublish, UUID founderID)
295 {
296 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
297
298 UUID GroupID = UUID.Random();
299 UUID OwnerRoleID = UUID.Random();
300
301 OSDMap GroupInfoMap = new OSDMap();
302 GroupInfoMap["Charter"] = OSD.FromString(charter);
303 GroupInfoMap["ShowInList"] = OSD.FromBoolean(showInList);
304 GroupInfoMap["InsigniaID"] = OSD.FromUUID(insigniaID);
305 GroupInfoMap["MembershipFee"] = OSD.FromInteger(0);
306 GroupInfoMap["OpenEnrollment"] = OSD.FromBoolean(openEnrollment);
307 GroupInfoMap["AllowPublish"] = OSD.FromBoolean(allowPublish);
308 GroupInfoMap["MaturePublish"] = OSD.FromBoolean(maturePublish);
309 GroupInfoMap["FounderID"] = OSD.FromUUID(founderID);
310 GroupInfoMap["EveryonePowers"] = OSD.FromULong((ulong)m_DefaultEveryonePowers);
311 GroupInfoMap["OwnerRoleID"] = OSD.FromUUID(OwnerRoleID);
312 GroupInfoMap["OwnersPowers"] = OSD.FromULong((ulong)m_DefaultOwnerPowers);
313
314 if (SimianAddGeneric(GroupID, "Group", name, GroupInfoMap))
315 {
316 AddGroupRole(requestingAgentID, GroupID, UUID.Zero, "Everyone", "Members of " + name, "Member of " + name, (ulong)m_DefaultEveryonePowers);
317 AddGroupRole(requestingAgentID, GroupID, OwnerRoleID, "Owners", "Owners of " + name, "Owner of " + name, (ulong)m_DefaultOwnerPowers);
318
319 AddAgentToGroup(requestingAgentID, requestingAgentID, GroupID, OwnerRoleID);
320
321 return GroupID;
322 }
323 else
324 {
325 return UUID.Zero;
326 }
327 }
328
329
330 public void UpdateGroup(UUID requestingAgentID, UUID groupID, string charter, bool showInList,
331 UUID insigniaID, int membershipFee, bool openEnrollment,
332 bool allowPublish, bool maturePublish)
333 {
334 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
335 // TODO: Check to make sure requestingAgentID has permission to update group
336
337 string GroupName;
338 OSDMap GroupInfoMap;
339 if (SimianGetFirstGenericEntry(groupID, "GroupInfo", out GroupName, out GroupInfoMap))
340 {
341 GroupInfoMap["Charter"] = OSD.FromString(charter);
342 GroupInfoMap["ShowInList"] = OSD.FromBoolean(showInList);
343 GroupInfoMap["InsigniaID"] = OSD.FromUUID(insigniaID);
344 GroupInfoMap["MembershipFee"] = OSD.FromInteger(0);
345 GroupInfoMap["OpenEnrollment"] = OSD.FromBoolean(openEnrollment);
346 GroupInfoMap["AllowPublish"] = OSD.FromBoolean(allowPublish);
347 GroupInfoMap["MaturePublish"] = OSD.FromBoolean(maturePublish);
348
349 SimianAddGeneric(groupID, "Group", GroupName, GroupInfoMap);
350 }
351
352 }
353
354
355 public void AddGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description,
356 string title, ulong powers)
357 {
358 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
359
360 OSDMap GroupRoleInfo = new OSDMap();
361 GroupRoleInfo["Name"] = OSD.FromString(name);
362 GroupRoleInfo["Description"] = OSD.FromString(description);
363 GroupRoleInfo["Title"] = OSD.FromString(title);
364 GroupRoleInfo["Powers"] = OSD.FromULong((ulong)powers);
365
366 // TODO: Add security, make sure that requestingAgentID has permision to add roles
367 SimianAddGeneric(groupID, "GroupRole", roleID.ToString(), GroupRoleInfo);
368 }
369
370 public void RemoveGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID)
371 {
372 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
373
374 // TODO: Add security
375
376 // Can't delete the Everyone Role
377 if (roleID != UUID.Zero)
378 {
379 // Remove all GroupRole Members from Role
380 Dictionary<UUID, OSDMap> GroupRoleMembers;
381 string GroupRoleMemberType = "GroupRole" + groupID.ToString();
382 if (SimianGetGenericEntries(GroupRoleMemberType, roleID.ToString(), out GroupRoleMembers))
383 {
384 foreach (UUID UserID in GroupRoleMembers.Keys)
385 {
386 EnsureRoleNotSelectedByMember(groupID, roleID, UserID);
387
388 SimianRemoveGenericEntry(UserID, GroupRoleMemberType, roleID.ToString());
389 }
390 }
391
392 // Remove role
393 SimianRemoveGenericEntry(groupID, "GroupRole", roleID.ToString());
394 }
395 }
396
397
398 public void UpdateGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description,
399 string title, ulong powers)
400 {
401 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
402
403 // TODO: Security, check that requestingAgentID is allowed to update group roles
404
405 OSDMap GroupRoleInfo;
406 if (SimianGetGenericEntry(groupID, "GroupRole", roleID.ToString(), out GroupRoleInfo))
407 {
408 if (name != null)
409 {
410 GroupRoleInfo["Name"] = OSD.FromString(name);
411 }
412 if (description != null)
413 {
414 GroupRoleInfo["Description"] = OSD.FromString(description);
415 }
416 if (title != null)
417 {
418 GroupRoleInfo["Title"] = OSD.FromString(title);
419 }
420 GroupRoleInfo["Powers"] = OSD.FromULong((ulong)powers);
421
422 }
423
424
425 SimianAddGeneric(groupID, "GroupRole", roleID.ToString(), GroupRoleInfo);
426 }
427
428 public GroupRecord GetGroupRecord(UUID requestingAgentID, UUID groupID, string groupName)
429 {
430 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
431
432 OSDMap GroupInfoMap = null;
433 if (groupID != UUID.Zero)
434 {
435 if (!SimianGetFirstGenericEntry(groupID, "Group", out groupName, out GroupInfoMap))
436 {
437 return null;
438 }
439 }
440 else if (!string.IsNullOrEmpty(groupName))
441 {
442 if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap))
443 {
444 return null;
445 }
446 }
447
448 GroupRecord GroupInfo = new GroupRecord();
449
450 GroupInfo.GroupID = groupID;
451 GroupInfo.GroupName = groupName;
452 GroupInfo.Charter = GroupInfoMap["Charter"].AsString();
453 GroupInfo.ShowInList = GroupInfoMap["ShowInList"].AsBoolean();
454 GroupInfo.GroupPicture = GroupInfoMap["InsigniaID"].AsUUID();
455 GroupInfo.MembershipFee = GroupInfoMap["MembershipFee"].AsInteger();
456 GroupInfo.OpenEnrollment = GroupInfoMap["OpenEnrollment"].AsBoolean();
457 GroupInfo.AllowPublish = GroupInfoMap["AllowPublish"].AsBoolean();
458 GroupInfo.MaturePublish = GroupInfoMap["MaturePublish"].AsBoolean();
459 GroupInfo.FounderID = GroupInfoMap["FounderID"].AsUUID();
460 GroupInfo.OwnerRoleID = GroupInfoMap["OwnerRoleID"].AsUUID();
461
462 return GroupInfo;
463
464 }
465
466 public GroupProfileData GetMemberGroupProfile(UUID requestingAgentID, UUID groupID, UUID memberID)
467 {
468 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
469
470 OSDMap groupProfile;
471 string groupName;
472 if (!SimianGetFirstGenericEntry(groupID, "Group", out groupName, out groupProfile))
473 {
474 // GroupProfileData is not nullable
475 return new GroupProfileData();
476 }
477
478 GroupProfileData MemberGroupProfile = new GroupProfileData();
479 MemberGroupProfile.GroupID = groupID;
480 MemberGroupProfile.Name = groupName;
481
482 if (groupProfile["Charter"] != null)
483 {
484 MemberGroupProfile.Charter = groupProfile["Charter"].AsString();
485 }
486
487 MemberGroupProfile.ShowInList = groupProfile["ShowInList"].AsString() == "1";
488 MemberGroupProfile.InsigniaID = groupProfile["InsigniaID"].AsUUID();
489 MemberGroupProfile.MembershipFee = groupProfile["MembershipFee"].AsInteger();
490 MemberGroupProfile.OpenEnrollment = groupProfile["OpenEnrollment"].AsBoolean();
491 MemberGroupProfile.AllowPublish = groupProfile["AllowPublish"].AsBoolean();
492 MemberGroupProfile.MaturePublish = groupProfile["MaturePublish"].AsBoolean();
493 MemberGroupProfile.FounderID = groupProfile["FounderID"].AsUUID();;
494 MemberGroupProfile.OwnerRole = groupProfile["OwnerRoleID"].AsUUID();
495
496 Dictionary<UUID, OSDMap> Members;
497 if (SimianGetGenericEntries("GroupMember",groupID.ToString(), out Members))
498 {
499 MemberGroupProfile.GroupMembershipCount = Members.Count;
500 }
501
502 Dictionary<string, OSDMap> Roles;
503 if (SimianGetGenericEntries(groupID, "GroupRole", out Roles))
504 {
505 MemberGroupProfile.GroupRolesCount = Roles.Count;
506 }
507
508 // TODO: Get Group Money balance from somewhere
509 // group.Money = 0;
510
511 GroupMembershipData MemberInfo = GetAgentGroupMembership(requestingAgentID, memberID, groupID);
512
513 MemberGroupProfile.MemberTitle = MemberInfo.GroupTitle;
514 MemberGroupProfile.PowersMask = MemberInfo.GroupPowers;
515
516 return MemberGroupProfile;
517 }
518
519 public void SetAgentActiveGroup(UUID requestingAgentID, UUID agentID, UUID groupID)
520 {
521 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
522
523 OSDMap ActiveGroup = new OSDMap();
524 ActiveGroup.Add("GroupID", OSD.FromUUID(groupID));
525 SimianAddGeneric(agentID, "Group", "ActiveGroup", ActiveGroup);
526 }
527
528 public void SetAgentActiveGroupRole(UUID requestingAgentID, UUID agentID, UUID groupID, UUID roleID)
529 {
530 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
531
532 OSDMap GroupMemberInfo;
533 if (!SimianGetGenericEntry(agentID, "GroupMember", groupID.ToString(), out GroupMemberInfo))
534 {
535 GroupMemberInfo = new OSDMap();
536 }
537
538 GroupMemberInfo["SelectedRoleID"] = OSD.FromUUID(roleID);
539 SimianAddGeneric(agentID, "GroupMember", groupID.ToString(), GroupMemberInfo);
540 }
541
542 public void SetAgentGroupInfo(UUID requestingAgentID, UUID agentID, UUID groupID, bool acceptNotices, bool listInProfile)
543 {
544 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
545
546 OSDMap GroupMemberInfo;
547 if (!SimianGetGenericEntry(agentID, "GroupMember", groupID.ToString(), out GroupMemberInfo))
548 {
549 GroupMemberInfo = new OSDMap();
550 }
551
552 GroupMemberInfo["AcceptNotices"] = OSD.FromBoolean(acceptNotices);
553 GroupMemberInfo["ListInProfile"] = OSD.FromBoolean(listInProfile);
554 GroupMemberInfo["Contribution"] = OSD.FromInteger(0);
555 GroupMemberInfo["SelectedRole"] = OSD.FromUUID(UUID.Zero);
556 SimianAddGeneric(agentID, "GroupMember", groupID.ToString(), GroupMemberInfo);
557 }
558
559 public void AddAgentToGroupInvite(UUID requestingAgentID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID)
560 {
561 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
562
563 OSDMap Invite = new OSDMap();
564 Invite["AgentID"] = OSD.FromUUID(agentID);
565 Invite["RoleID"] = OSD.FromUUID(roleID);
566
567 SimianAddGeneric(groupID, "GroupMemberInvite", inviteID.ToString(), Invite);
568 }
569
570 public GroupInviteInfo GetAgentToGroupInvite(UUID requestingAgentID, UUID inviteID)
571 {
572 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
573
574 OSDMap GroupMemberInvite;
575 UUID GroupID;
576 if (!SimianGetFirstGenericEntry("GroupMemberInvite", inviteID.ToString(), out GroupID, out GroupMemberInvite))
577 {
578 return null;
579 }
580
581 GroupInviteInfo inviteInfo = new GroupInviteInfo();
582 inviteInfo.InviteID = inviteID;
583 inviteInfo.GroupID = GroupID;
584 inviteInfo.AgentID = GroupMemberInvite["AgentID"].AsUUID();
585 inviteInfo.RoleID = GroupMemberInvite["RoleID"].AsUUID();
586
587 return inviteInfo;
588 }
589
590 public void RemoveAgentToGroupInvite(UUID requestingAgentID, UUID inviteID)
591 {
592 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
593
594 GroupInviteInfo invite = GetAgentToGroupInvite(requestingAgentID, inviteID);
595 SimianRemoveGenericEntry(invite.GroupID, "GroupMemberInvite", inviteID.ToString());
596 }
597
598 public void AddAgentToGroup(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID)
599 {
600 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
601
602 // Setup Agent/Group information
603 SetAgentGroupInfo(requestingAgentID, AgentID, GroupID, true, true);
604
605 // Add agent to Everyone Group
606 AddAgentToGroupRole(requestingAgentID, AgentID, GroupID, UUID.Zero);
607
608 // Add agent to Specified Role
609 AddAgentToGroupRole(requestingAgentID, AgentID, GroupID, RoleID);
610
611 // Set selected role in this group to specified role
612 SetAgentActiveGroupRole(requestingAgentID, AgentID, GroupID, RoleID);
613 }
614
615 public void RemoveAgentFromGroup(UUID requestingAgentID, UUID agentID, UUID groupID)
616 {
617 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
618
619 // If current active group is the group the agent is being removed from, change their group to UUID.Zero
620 GroupMembershipData memberActiveMembership = GetAgentActiveMembership(requestingAgentID, agentID);
621 if (memberActiveMembership.GroupID == groupID)
622 {
623 SetAgentActiveGroup(agentID, agentID, UUID.Zero);
624 }
625
626 // Remove Group Member information for this group
627 SimianRemoveGenericEntry(agentID, "GroupMember", groupID.ToString());
628
629 // By using a Simian Generics Type consisting of a prefix and a groupID,
630 // combined with RoleID as key allows us to get a list of roles a particular member
631 // of a group is assigned to.
632 string GroupRoleMemberType = "GroupRole" + groupID.ToString();
633
634 // Take Agent out of all other group roles
635 Dictionary<string, OSDMap> GroupRoles;
636 if (SimianGetGenericEntries(agentID, GroupRoleMemberType, out GroupRoles))
637 {
638 foreach (string roleID in GroupRoles.Keys)
639 {
640 SimianRemoveGenericEntry(agentID, GroupRoleMemberType, roleID);
641 }
642 }
643 }
644
645 public void AddAgentToGroupRole(UUID requestingAgentID, UUID agentID, UUID groupID, UUID roleID)
646 {
647 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
648
649 SimianAddGeneric(agentID, "GroupRole" + groupID.ToString(), roleID.ToString(), new OSDMap());
650 }
651
652 public void RemoveAgentFromGroupRole(UUID requestingAgentID, UUID agentID, UUID groupID, UUID roleID)
653 {
654 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
655
656 // Cannot remove members from the Everyone Role
657 if (roleID != UUID.Zero)
658 {
659 EnsureRoleNotSelectedByMember(groupID, roleID, agentID);
660
661 string GroupRoleMemberType = "GroupRole" + groupID.ToString();
662 SimianRemoveGenericEntry(agentID, GroupRoleMemberType, roleID.ToString());
663 }
664 }
665
666 public List<DirGroupsReplyData> FindGroups(UUID requestingAgentID, string search)
667 {
668 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
669
670 List<DirGroupsReplyData> findings = new List<DirGroupsReplyData>();
671
672 NameValueCollection requestArgs = new NameValueCollection
673 {
674 { "RequestMethod", "GetGenerics" },
675 { "Type", "Group" },
676 { "Key", search },
677 { "Fuzzy", "1" }
678 };
679
680
681 OSDMap response = CachedPostRequest(requestArgs);
682 if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
683 {
684 OSDArray entryArray = (OSDArray)response["Entries"];
685 foreach (OSDMap entryMap in entryArray)
686 {
687 DirGroupsReplyData data = new DirGroupsReplyData();
688 data.groupID = entryMap["OwnerID"].AsUUID();
689 data.groupName = entryMap["Key"].AsString();
690
691 // TODO: is there a better way to do this?
692 Dictionary<UUID, OSDMap> Members;
693 if (SimianGetGenericEntries("GroupMember", data.groupID.ToString(), out Members))
694 {
695 data.members = Members.Count;
696 }
697 else
698 {
699 data.members = 0;
700 }
701
702 // TODO: sort results?
703 // data.searchOrder = order;
704
705 findings.Add(data);
706 }
707 }
708
709 return findings;
710 }
711
712 public GroupMembershipData GetAgentGroupMembership(UUID requestingAgentID, UUID agentID, UUID groupID)
713 {
714 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
715
716 GroupMembershipData data = null;
717// bool foundData = false;
718
719 OSDMap UserGroupMemberInfo;
720 if (SimianGetGenericEntry(agentID, "GroupMember", groupID.ToString(), out UserGroupMemberInfo))
721 {
722 data = new GroupMembershipData();
723 data.AcceptNotices = UserGroupMemberInfo["AcceptNotices"].AsBoolean();
724 data.Contribution = UserGroupMemberInfo["Contribution"].AsInteger();
725 data.ListInProfile = UserGroupMemberInfo["ListInProfile"].AsBoolean();
726 data.ActiveRole = UserGroupMemberInfo["SelectedRoleID"].AsUUID();
727
728 ///////////////////////////////
729 // Agent Specific Information:
730 //
731 OSDMap UserActiveGroup;
732 if (SimianGetGenericEntry(agentID, "Group", "ActiveGroup", out UserActiveGroup))
733 {
734 data.Active = UserActiveGroup["GroupID"].AsUUID().Equals(groupID);
735 }
736
737 ///////////////////////////////
738 // Role Specific Information:
739 //
740 OSDMap GroupRoleInfo;
741 if (SimianGetGenericEntry(groupID, "GroupRole", data.ActiveRole.ToString(), out GroupRoleInfo))
742 {
743 data.GroupTitle = GroupRoleInfo["Title"].AsString();
744 data.GroupPowers = GroupRoleInfo["Powers"].AsULong();
745 }
746
747 ///////////////////////////////
748 // Group Specific Information:
749 //
750 OSDMap GroupInfo;
751 string GroupName;
752 if (SimianGetFirstGenericEntry(groupID, "Group", out GroupName, out GroupInfo))
753 {
754 data.GroupID = groupID;
755 data.AllowPublish = GroupInfo["AllowPublish"].AsBoolean();
756 data.Charter = GroupInfo["Charter"].AsString();
757 data.FounderID = GroupInfo["FounderID"].AsUUID();
758 data.GroupName = GroupName;
759 data.GroupPicture = GroupInfo["InsigniaID"].AsUUID();
760 data.MaturePublish = GroupInfo["MaturePublish"].AsBoolean();
761 data.MembershipFee = GroupInfo["MembershipFee"].AsInteger();
762 data.OpenEnrollment = GroupInfo["OpenEnrollment"].AsBoolean();
763 data.ShowInList = GroupInfo["ShowInList"].AsBoolean();
764 }
765 }
766
767 return data;
768 }
769
770 public GroupMembershipData GetAgentActiveMembership(UUID requestingAgentID, UUID agentID)
771 {
772 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
773
774 UUID GroupID = UUID.Zero;
775 OSDMap UserActiveGroup;
776 if (SimianGetGenericEntry(agentID, "Group", "ActiveGroup", out UserActiveGroup))
777 {
778 GroupID = UserActiveGroup["GroupID"].AsUUID();
779 }
780
781 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Active GroupID : {0}", GroupID.ToString());
782 return GetAgentGroupMembership(requestingAgentID, agentID, GroupID);
783 }
784
785 public List<GroupMembershipData> GetAgentGroupMemberships(UUID requestingAgentID, UUID agentID)
786 {
787 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
788
789 List<GroupMembershipData> memberships = new List<GroupMembershipData>();
790
791 Dictionary<string,OSDMap> GroupMemberShips;
792 if (SimianGetGenericEntries(agentID, "GroupMember", out GroupMemberShips))
793 {
794 foreach (string key in GroupMemberShips.Keys)
795 {
796 memberships.Add(GetAgentGroupMembership(requestingAgentID, agentID, UUID.Parse(key)));
797 }
798 }
799
800 return memberships;
801 }
802
803 public List<GroupRolesData> GetAgentGroupRoles(UUID requestingAgentID, UUID agentID, UUID groupID)
804 {
805 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
806
807 List<GroupRolesData> Roles = new List<GroupRolesData>();
808
809 Dictionary<string, OSDMap> GroupRoles;
810 if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles))
811 {
812 Dictionary<string, OSDMap> MemberRoles;
813 if (SimianGetGenericEntries(agentID, "GroupRole" + groupID.ToString(), out MemberRoles))
814 {
815 foreach (KeyValuePair<string, OSDMap> kvp in MemberRoles)
816 {
817 GroupRolesData data = new GroupRolesData();
818 data.RoleID = UUID.Parse(kvp.Key);
819 data.Name = GroupRoles[kvp.Key]["Name"].AsString();
820 data.Description = GroupRoles[kvp.Key]["Description"].AsString();
821 data.Title = GroupRoles[kvp.Key]["Title"].AsString();
822 data.Powers = GroupRoles[kvp.Key]["Powers"].AsULong();
823
824 Roles.Add(data);
825 }
826 }
827 }
828 return Roles;
829 }
830
831 public List<GroupRolesData> GetGroupRoles(UUID requestingAgentID, UUID groupID)
832 {
833 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
834
835 List<GroupRolesData> Roles = new List<GroupRolesData>();
836
837 Dictionary<string, OSDMap> GroupRoles;
838 if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles))
839 {
840 foreach (KeyValuePair<string, OSDMap> role in GroupRoles)
841 {
842 GroupRolesData data = new GroupRolesData();
843
844 data.RoleID = UUID.Parse(role.Key);
845
846 data.Name = role.Value["Name"].AsString();
847 data.Description = role.Value["Description"].AsString();
848 data.Title = role.Value["Title"].AsString();
849 data.Powers = role.Value["Powers"].AsULong();
850
851 Dictionary<UUID, OSDMap> GroupRoleMembers;
852 if (SimianGetGenericEntries("GroupRole" + groupID.ToString(), role.Key, out GroupRoleMembers))
853 {
854 data.Members = GroupRoleMembers.Count;
855 }
856 else
857 {
858 data.Members = 0;
859 }
860
861 Roles.Add(data);
862 }
863 }
864
865 return Roles;
866
867 }
868
869
870
871 public List<GroupMembersData> GetGroupMembers(UUID requestingAgentID, UUID GroupID)
872 {
873 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
874
875 List<GroupMembersData> members = new List<GroupMembersData>();
876
877 OSDMap GroupInfo;
878 string GroupName;
879 UUID GroupOwnerRoleID = UUID.Zero;
880 if (!SimianGetFirstGenericEntry(GroupID, "Group", out GroupName, out GroupInfo))
881 {
882 return members;
883 }
884 GroupOwnerRoleID = GroupInfo["OwnerRoleID"].AsUUID();
885
886 // Locally cache group roles, since we'll be needing this data for each member
887 Dictionary<string,OSDMap> GroupRoles;
888 SimianGetGenericEntries(GroupID, "GroupRole", out GroupRoles);
889
890 // Locally cache list of group owners
891 Dictionary<UUID, OSDMap> GroupOwners;
892 SimianGetGenericEntries("GroupRole" + GroupID.ToString(), GroupOwnerRoleID.ToString(), out GroupOwners);
893
894
895 Dictionary<UUID, OSDMap> GroupMembers;
896 if (SimianGetGenericEntries("GroupMember", GroupID.ToString(), out GroupMembers))
897 {
898 foreach (KeyValuePair<UUID, OSDMap> member in GroupMembers)
899 {
900 GroupMembersData data = new GroupMembersData();
901
902 data.AgentID = member.Key;
903
904 UUID SelectedRoleID = member.Value["SelectedRoleID"].AsUUID();
905
906 data.AcceptNotices = member.Value["AcceptNotices"].AsBoolean();
907 data.ListInProfile = member.Value["ListInProfile"].AsBoolean();
908 data.Contribution = member.Value["Contribution"].AsInteger();
909
910 data.IsOwner = GroupOwners.ContainsKey(member.Key);
911
912 OSDMap GroupRoleInfo = GroupRoles[SelectedRoleID.ToString()];
913 data.Title = GroupRoleInfo["Title"].AsString();
914 data.AgentPowers = GroupRoleInfo["Powers"].AsULong();
915
916 members.Add(data);
917 }
918 }
919
920 return members;
921
922 }
923
924 public List<GroupRoleMembersData> GetGroupRoleMembers(UUID requestingAgentID, UUID groupID)
925 {
926 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
927
928 List<GroupRoleMembersData> members = new List<GroupRoleMembersData>();
929
930 Dictionary<string, OSDMap> GroupRoles;
931 if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles))
932 {
933 foreach (KeyValuePair<string, OSDMap> Role in GroupRoles)
934 {
935 Dictionary<UUID, OSDMap> GroupRoleMembers;
936 if (SimianGetGenericEntries("GroupRole"+groupID.ToString(), Role.Key, out GroupRoleMembers))
937 {
938 foreach (KeyValuePair<UUID, OSDMap> GroupRoleMember in GroupRoleMembers)
939 {
940 GroupRoleMembersData data = new GroupRoleMembersData();
941
942 data.MemberID = GroupRoleMember.Key;
943 data.RoleID = UUID.Parse(Role.Key);
944
945 members.Add(data);
946 }
947 }
948 }
949 }
950
951 return members;
952 }
953
954 public List<GroupNoticeData> GetGroupNotices(UUID requestingAgentID, UUID GroupID)
955 {
956 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
957
958 List<GroupNoticeData> values = new List<GroupNoticeData>();
959
960 Dictionary<string, OSDMap> Notices;
961 if (SimianGetGenericEntries(GroupID, "GroupNotice", out Notices))
962 {
963 foreach (KeyValuePair<string, OSDMap> Notice in Notices)
964 {
965 GroupNoticeData data = new GroupNoticeData();
966 data.NoticeID = UUID.Parse(Notice.Key);
967 data.Timestamp = Notice.Value["TimeStamp"].AsUInteger();
968 data.FromName = Notice.Value["FromName"].AsString();
969 data.Subject = Notice.Value["Subject"].AsString();
970 data.HasAttachment = Notice.Value["BinaryBucket"].AsBinary().Length > 0;
971
972 //TODO: Figure out how to get this
973 data.AssetType = 0;
974
975 values.Add(data);
976 }
977 }
978
979 return values;
980
981 }
982 public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID)
983 {
984 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
985
986 OSDMap GroupNotice;
987 UUID GroupID;
988 if (SimianGetFirstGenericEntry("GroupNotice", noticeID.ToString(), out GroupID, out GroupNotice))
989 {
990 GroupNoticeInfo data = new GroupNoticeInfo();
991 data.GroupID = GroupID;
992 data.Message = GroupNotice["Message"].AsString();
993 data.BinaryBucket = GroupNotice["BinaryBucket"].AsBinary();
994 data.noticeData.NoticeID = noticeID;
995 data.noticeData.Timestamp = GroupNotice["TimeStamp"].AsUInteger();
996 data.noticeData.FromName = GroupNotice["FromName"].AsString();
997 data.noticeData.Subject = GroupNotice["Subject"].AsString();
998 data.noticeData.HasAttachment = data.BinaryBucket.Length > 0;
999 data.noticeData.AssetType = 0;
1000
1001 if (data.Message == null)
1002 {
1003 data.Message = string.Empty;
1004 }
1005
1006 return data;
1007 }
1008 return null;
1009 }
1010 public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket)
1011 {
1012 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1013
1014 OSDMap Notice = new OSDMap();
1015 Notice["TimeStamp"] = OSD.FromUInteger((uint)Util.UnixTimeSinceEpoch());
1016 Notice["FromName"] = OSD.FromString(fromName);
1017 Notice["Subject"] = OSD.FromString(subject);
1018 Notice["Message"] = OSD.FromString(message);
1019 Notice["BinaryBucket"] = OSD.FromBinary(binaryBucket);
1020
1021 SimianAddGeneric(groupID, "GroupNotice", noticeID.ToString(), Notice);
1022
1023 }
1024 #endregion
1025
1026 #region GroupSessionTracking
1027
1028 public void ResetAgentGroupChatSessions(UUID agentID)
1029 {
1030 Dictionary<string, OSDMap> agentSessions;
1031
1032 if (SimianGetGenericEntries(agentID, "GroupSessionDropped", out agentSessions))
1033 {
1034 foreach (string GroupID in agentSessions.Keys)
1035 {
1036 SimianRemoveGenericEntry(agentID, "GroupSessionDropped", GroupID);
1037 }
1038 }
1039
1040 if (SimianGetGenericEntries(agentID, "GroupSessionInvited", out agentSessions))
1041 {
1042 foreach (string GroupID in agentSessions.Keys)
1043 {
1044 SimianRemoveGenericEntry(agentID, "GroupSessionInvited", GroupID);
1045 }
1046 }
1047 }
1048
1049 public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID)
1050 {
1051 OSDMap session;
1052 return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session);
1053 }
1054
1055 public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID)
1056 {
1057 SimianAddGeneric(agentID, "GroupSessionDropped", groupID.ToString(), new OSDMap());
1058 }
1059
1060 public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID)
1061 {
1062 SimianAddGeneric(agentID, "GroupSessionInvited", groupID.ToString(), new OSDMap());
1063 }
1064
1065 public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID)
1066 {
1067 OSDMap session;
1068 return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session);
1069 }
1070
1071 #endregion
1072
1073 private void EnsureRoleNotSelectedByMember(UUID groupID, UUID roleID, UUID userID)
1074 {
1075 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1076
1077 // If member's SelectedRole is roleID, change their selected role to Everyone
1078 // before removing them from the role
1079 OSDMap UserGroupInfo;
1080 if (SimianGetGenericEntry(userID, "GroupMember", groupID.ToString(), out UserGroupInfo))
1081 {
1082 if (UserGroupInfo["SelectedRoleID"].AsUUID() == roleID)
1083 {
1084 UserGroupInfo["SelectedRoleID"] = OSD.FromUUID(UUID.Zero);
1085 }
1086 SimianAddGeneric(userID, "GroupMember", groupID.ToString(), UserGroupInfo);
1087 }
1088 }
1089
1090
1091 #region Simian Util Methods
1092 private bool SimianAddGeneric(UUID ownerID, string type, string key, OSDMap map)
1093 {
1094 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2},{3})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type, key);
1095
1096 string value = OSDParser.SerializeJsonString(map);
1097
1098 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] value: {0}", value);
1099
1100 NameValueCollection RequestArgs = new NameValueCollection
1101 {
1102 { "RequestMethod", "AddGeneric" },
1103 { "OwnerID", ownerID.ToString() },
1104 { "Type", type },
1105 { "Key", key },
1106 { "Value", value}
1107 };
1108
1109
1110 OSDMap Response = CachedPostRequest(RequestArgs);
1111 if (Response["Success"].AsBoolean())
1112 {
1113 return true;
1114 }
1115 else
1116 {
1117 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error {0}, {1}, {2}, {3}", ownerID, type, key, Response["Message"]);
1118 return false;
1119 }
1120 }
1121
1122 /// <summary>
1123 /// Returns the first of possibly many entries for Owner/Type pair
1124 /// </summary>
1125 private bool SimianGetFirstGenericEntry(UUID ownerID, string type, out string key, out OSDMap map)
1126 {
1127 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type);
1128
1129 NameValueCollection RequestArgs = new NameValueCollection
1130 {
1131 { "RequestMethod", "GetGenerics" },
1132 { "OwnerID", ownerID.ToString() },
1133 { "Type", type }
1134 };
1135
1136
1137 OSDMap Response = CachedPostRequest(RequestArgs);
1138 if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray)
1139 {
1140 OSDArray entryArray = (OSDArray)Response["Entries"];
1141 if (entryArray.Count >= 1)
1142 {
1143 OSDMap entryMap = entryArray[0] as OSDMap;
1144 key = entryMap["Key"].AsString();
1145 map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString());
1146
1147 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString());
1148
1149 return true;
1150 }
1151 else
1152 {
1153 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results");
1154 }
1155 }
1156 else
1157 {
1158 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", Response["Message"]);
1159 }
1160 key = null;
1161 map = null;
1162 return false;
1163 }
1164 private bool SimianGetFirstGenericEntry(string type, string key, out UUID ownerID, out OSDMap map)
1165 {
1166 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name, type, key);
1167
1168
1169 NameValueCollection RequestArgs = new NameValueCollection
1170 {
1171 { "RequestMethod", "GetGenerics" },
1172 { "Type", type },
1173 { "Key", key}
1174 };
1175
1176
1177 OSDMap Response = CachedPostRequest(RequestArgs);
1178 if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray)
1179 {
1180 OSDArray entryArray = (OSDArray)Response["Entries"];
1181 if (entryArray.Count >= 1)
1182 {
1183 OSDMap entryMap = entryArray[0] as OSDMap;
1184 ownerID = entryMap["OwnerID"].AsUUID();
1185 map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString());
1186
1187 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString());
1188
1189 return true;
1190 }
1191 else
1192 {
1193 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results");
1194 }
1195 }
1196 else
1197 {
1198 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", Response["Message"]);
1199 }
1200 ownerID = UUID.Zero;
1201 map = null;
1202 return false;
1203 }
1204
1205 private bool SimianGetGenericEntry(UUID ownerID, string type, string key, out OSDMap map)
1206 {
1207 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2},{3})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type, key);
1208
1209 NameValueCollection RequestArgs = new NameValueCollection
1210 {
1211 { "RequestMethod", "GetGenerics" },
1212 { "OwnerID", ownerID.ToString() },
1213 { "Type", type },
1214 { "Key", key}
1215 };
1216
1217
1218 OSDMap Response = CachedPostRequest(RequestArgs);
1219 if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray)
1220 {
1221 OSDArray entryArray = (OSDArray)Response["Entries"];
1222 if (entryArray.Count == 1)
1223 {
1224 OSDMap entryMap = entryArray[0] as OSDMap;
1225 key = entryMap["Key"].AsString();
1226 map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString());
1227
1228 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString());
1229
1230 return true;
1231 }
1232 else
1233 {
1234 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results");
1235 }
1236 }
1237 else
1238 {
1239 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", Response["Message"]);
1240 }
1241 map = null;
1242 return false;
1243 }
1244
1245 private bool SimianGetGenericEntries(UUID ownerID, string type, out Dictionary<string, OSDMap> maps)
1246 {
1247 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name,ownerID, type);
1248
1249 NameValueCollection requestArgs = new NameValueCollection
1250 {
1251 { "RequestMethod", "GetGenerics" },
1252 { "OwnerID", ownerID.ToString() },
1253 { "Type", type }
1254 };
1255
1256
1257
1258 OSDMap response = CachedPostRequest(requestArgs);
1259 if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
1260 {
1261 maps = new Dictionary<string, OSDMap>();
1262
1263 OSDArray entryArray = (OSDArray)response["Entries"];
1264 foreach (OSDMap entryMap in entryArray)
1265 {
1266 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString());
1267 maps.Add(entryMap["Key"].AsString(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()));
1268 }
1269 if (maps.Count == 0)
1270 {
1271 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results");
1272 }
1273
1274 return true;
1275 }
1276 else
1277 {
1278 maps = null;
1279 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error retrieving group info ({0})", response["Message"]);
1280 }
1281 return false;
1282 }
1283 private bool SimianGetGenericEntries(string type, string key, out Dictionary<UUID, OSDMap> maps)
1284 {
1285 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2})", System.Reflection.MethodBase.GetCurrentMethod().Name, type, key);
1286
1287 NameValueCollection requestArgs = new NameValueCollection
1288 {
1289 { "RequestMethod", "GetGenerics" },
1290 { "Type", type },
1291 { "Key", key }
1292 };
1293
1294
1295
1296 OSDMap response = CachedPostRequest(requestArgs);
1297 if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
1298 {
1299 maps = new Dictionary<UUID, OSDMap>();
1300
1301 OSDArray entryArray = (OSDArray)response["Entries"];
1302 foreach (OSDMap entryMap in entryArray)
1303 {
1304 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString());
1305 maps.Add(entryMap["OwnerID"].AsUUID(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()));
1306 }
1307 if (maps.Count == 0)
1308 {
1309 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results");
1310 }
1311 return true;
1312 }
1313 else
1314 {
1315 maps = null;
1316 m_log.WarnFormat("[SIMIAN-GROUPS-CONNECTOR]: Error retrieving group info ({0})", response["Message"]);
1317 }
1318 return false;
1319 }
1320
1321 private bool SimianRemoveGenericEntry(UUID ownerID, string type, string key)
1322 {
1323 if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] {0} called ({1},{2},{3})", System.Reflection.MethodBase.GetCurrentMethod().Name, ownerID, type, key);
1324
1325 NameValueCollection requestArgs = new NameValueCollection
1326 {
1327 { "RequestMethod", "RemoveGeneric" },
1328 { "OwnerID", ownerID.ToString() },
1329 { "Type", type },
1330 { "Key", key }
1331 };
1332
1333
1334 OSDMap response = CachedPostRequest(requestArgs);
1335 if (response["Success"].AsBoolean())
1336 {
1337 return true;
1338 }
1339 else
1340 {
1341 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: Error {0}, {1}, {2}, {3}", ownerID, type, key, response["Message"]);
1342 return false;
1343 }
1344 }
1345 #endregion
1346
1347 #region CheesyCache
1348 OSDMap CachedPostRequest(NameValueCollection requestArgs)
1349 {
1350 // Immediately forward the request if the cache is disabled.
1351 if (m_cacheTimeout == 0)
1352 {
1353 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: cache is disabled");
1354 return WebUtil.PostToService(m_groupsServerURI, requestArgs);
1355 }
1356
1357 // Check if this is an update or a request
1358 if (requestArgs["RequestMethod"] == "RemoveGeneric"
1359 || requestArgs["RequestMethod"] == "AddGeneric")
1360 {
1361 m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: clearing generics cache");
1362
1363 // Any and all updates cause the cache to clear
1364 m_memoryCache.Clear();
1365
1366 // Send update to server, return the response without caching it
1367 return WebUtil.PostToService(m_groupsServerURI, requestArgs);
1368 }
1369
1370 // If we're not doing an update, we must be requesting data
1371
1372 // Create the cache key for the request and see if we have it cached
1373 string CacheKey = WebUtil.BuildQueryString(requestArgs);
1374
1375 // This code uses a leader/follower pattern. On a cache miss, the request is added
1376 // to a queue; the first thread to add it to the queue completes the request while
1377 // follow on threads busy wait for the results, this situation seems to happen
1378 // often when checking permissions
1379 while (true)
1380 {
1381 OSDMap response = null;
1382 bool firstRequest = false;
1383
1384 lock (m_memoryCache)
1385 {
1386 if (m_memoryCache.TryGetValue(CacheKey, out response))
1387 return response;
1388
1389 if (! m_pendingRequests.ContainsKey(CacheKey))
1390 {
1391 m_pendingRequests.Add(CacheKey,true);
1392 firstRequest = true;
1393 }
1394 }
1395
1396 if (firstRequest)
1397 {
1398 // if it wasn't in the cache, pass the request to the Simian Grid Services
1399 try
1400 {
1401 response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
1402 }
1403 catch (Exception)
1404 {
1405 m_log.ErrorFormat("[SIMIAN GROUPS CONNECTOR]: request failed {0}", CacheKey);
1406 }
1407
1408 // and cache the response
1409 lock (m_memoryCache)
1410 {
1411 m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
1412 m_pendingRequests.Remove(CacheKey);
1413 }
1414
1415 return response;
1416 }
1417
1418 Thread.Sleep(50); // waiting for a web request to complete, 50msecs is reasonable
1419 }
1420
1421 // if (!m_memoryCache.TryGetValue(CacheKey, out response))
1422 // {
1423 // m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: query not in the cache");
1424 // Util.PrintCallStack();
1425
1426 // // if it wasn't in the cache, pass the request to the Simian Grid Services
1427 // response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
1428
1429 // // and cache the response
1430 // m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
1431 // }
1432
1433 // // return cached response
1434 // return response;
1435 }
1436 #endregion
1437
1438 }
1439
1440}
1441
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
new file mode 100644
index 0000000..e03e71d
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
@@ -0,0 +1,268 @@
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;
30using System.Collections.Generic;
31using System.Net;
32using System.Reflection;
33using Nini.Config;
34using NUnit.Framework;
35using OpenMetaverse;
36using OpenMetaverse.Messages.Linden;
37using OpenMetaverse.Packets;
38using OpenMetaverse.StructuredData;
39using OpenSim.Framework;
40using OpenSim.Framework.Communications;
41using OpenSim.Framework.Servers;
42using OpenSim.Framework.Servers.HttpServer;
43using OpenSim.Region.ClientStack.Linden;
44using OpenSim.Region.CoreModules.Avatar.InstantMessage;
45using OpenSim.Region.CoreModules.Framework;
46using OpenSim.Region.Framework.Scenes;
47using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
48using OpenSim.Tests.Common;
49
50namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests
51{
52 /// <summary>
53 /// Basic groups module tests
54 /// </summary>
55 [TestFixture]
56 public class GroupsModuleTests : OpenSimTestCase
57 {
58 [SetUp]
59 public override void SetUp()
60 {
61 base.SetUp();
62
63 uint port = 9999;
64 uint sslPort = 9998;
65
66 // This is an unfortunate bit of clean up we have to do because MainServer manages things through static
67 // variables and the VM is not restarted between tests.
68 MainServer.RemoveHttpServer(port);
69
70 BaseHttpServer server = new BaseHttpServer(port, false, sslPort, "");
71 MainServer.AddHttpServer(server);
72 MainServer.Instance = server;
73 }
74
75 [Test]
76 public void TestSendAgentGroupDataUpdate()
77 {
78 TestHelpers.InMethod();
79// TestHelpers.EnableLogging();
80
81 TestScene scene = new SceneHelpers().SetupScene();
82 IConfigSource configSource = new IniConfigSource();
83 IConfig config = configSource.AddConfig("Groups");
84 config.Set("Enabled", true);
85 config.Set("Module", "GroupsModule");
86 config.Set("DebugEnabled", true);
87
88 GroupsModule gm = new GroupsModule();
89 EventQueueGetModule eqgm = new EventQueueGetModule();
90
91 // We need a capabilities module active so that adding the scene presence creates an event queue in the
92 // EventQueueGetModule
93 SceneHelpers.SetupSceneModules(
94 scene, configSource, gm, new MockGroupsServicesConnector(), new CapabilitiesModule(), eqgm);
95
96 ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseStem("1"));
97
98 gm.SendAgentGroupDataUpdate(sp.ControllingClient);
99
100 Hashtable eventsResponse = eqgm.GetEvents(UUID.Zero, sp.UUID);
101
102 Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.OK));
103
104// Console.WriteLine("Response [{0}]", (string)eventsResponse["str_response_string"]);
105
106 OSDMap rawOsd = (OSDMap)OSDParser.DeserializeLLSDXml((string)eventsResponse["str_response_string"]);
107 OSDArray eventsOsd = (OSDArray)rawOsd["events"];
108
109 bool foundUpdate = false;
110 foreach (OSD osd in eventsOsd)
111 {
112 OSDMap eventOsd = (OSDMap)osd;
113
114 if (eventOsd["message"] == "AgentGroupDataUpdate")
115 foundUpdate = true;
116 }
117
118 Assert.That(foundUpdate, Is.True, "Did not find AgentGroupDataUpdate in response");
119
120 // TODO: More checking of more actual event data.
121 }
122
123 [Test]
124 public void TestSendGroupNotice()
125 {
126 TestHelpers.InMethod();
127// TestHelpers.EnableLogging();
128
129 TestScene scene = new SceneHelpers().SetupScene();
130
131 MessageTransferModule mtm = new MessageTransferModule();
132 GroupsModule gm = new GroupsModule();
133 GroupsMessagingModule gmm = new GroupsMessagingModule();
134 MockGroupsServicesConnector mgsc = new MockGroupsServicesConnector();
135
136 IConfigSource configSource = new IniConfigSource();
137
138 {
139 IConfig config = configSource.AddConfig("Messaging");
140 config.Set("MessageTransferModule", mtm.Name);
141 }
142
143 {
144 IConfig config = configSource.AddConfig("Groups");
145 config.Set("Enabled", true);
146 config.Set("Module", gm.Name);
147 config.Set("DebugEnabled", true);
148 config.Set("MessagingModule", gmm.Name);
149 config.Set("MessagingEnabled", true);
150 }
151
152 SceneHelpers.SetupSceneModules(scene, configSource, mgsc, mtm, gm, gmm);
153
154 UUID userId = TestHelpers.ParseTail(0x1);
155 string subjectText = "newman";
156 string messageText = "Hello";
157 string combinedSubjectMessage = string.Format("{0}|{1}", subjectText, messageText);
158
159 ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x1));
160 TestClient tc = (TestClient)sp.ControllingClient;
161
162 UUID groupID = gm.CreateGroup(tc, "group1", null, true, UUID.Zero, 0, true, true, true);
163 gm.JoinGroupRequest(tc, groupID);
164
165 // Create a second user who doesn't want to receive notices
166 ScenePresence sp2 = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x2));
167 TestClient tc2 = (TestClient)sp2.ControllingClient;
168 gm.JoinGroupRequest(tc2, groupID);
169 gm.SetGroupAcceptNotices(tc2, groupID, false, true);
170
171 List<GridInstantMessage> spReceivedMessages = new List<GridInstantMessage>();
172 tc.OnReceivedInstantMessage += im => spReceivedMessages.Add(im);
173
174 List<GridInstantMessage> sp2ReceivedMessages = new List<GridInstantMessage>();
175 tc2.OnReceivedInstantMessage += im => sp2ReceivedMessages.Add(im);
176
177 GridInstantMessage noticeIm = new GridInstantMessage();
178 noticeIm.fromAgentID = userId.Guid;
179 noticeIm.toAgentID = groupID.Guid;
180 noticeIm.message = combinedSubjectMessage;
181 noticeIm.dialog = (byte)InstantMessageDialog.GroupNotice;
182
183 tc.HandleImprovedInstantMessage(noticeIm);
184
185 Assert.That(spReceivedMessages.Count, Is.EqualTo(1));
186 Assert.That(spReceivedMessages[0].message, Is.EqualTo(combinedSubjectMessage));
187
188 List<GroupNoticeData> notices = mgsc.GetGroupNotices(UUID.Zero, groupID);
189 Assert.AreEqual(1, notices.Count);
190
191 // OpenSimulator (possibly also SL) transport the notice ID as the session ID!
192 Assert.AreEqual(notices[0].NoticeID.Guid, spReceivedMessages[0].imSessionID);
193
194 Assert.That(sp2ReceivedMessages.Count, Is.EqualTo(0));
195 }
196
197 /// <summary>
198 /// Run test with the MessageOnlineUsersOnly flag set.
199 /// </summary>
200 [Test]
201 public void TestSendGroupNoticeOnlineOnly()
202 {
203 TestHelpers.InMethod();
204 // TestHelpers.EnableLogging();
205
206 TestScene scene = new SceneHelpers().SetupScene();
207
208 MessageTransferModule mtm = new MessageTransferModule();
209 GroupsModule gm = new GroupsModule();
210 GroupsMessagingModule gmm = new GroupsMessagingModule();
211
212 IConfigSource configSource = new IniConfigSource();
213
214 {
215 IConfig config = configSource.AddConfig("Messaging");
216 config.Set("MessageTransferModule", mtm.Name);
217 }
218
219 {
220 IConfig config = configSource.AddConfig("Groups");
221 config.Set("Enabled", true);
222 config.Set("Module", gm.Name);
223 config.Set("DebugEnabled", true);
224 config.Set("MessagingModule", gmm.Name);
225 config.Set("MessagingEnabled", true);
226 config.Set("MessageOnlineUsersOnly", true);
227 }
228
229 SceneHelpers.SetupSceneModules(scene, configSource, new MockGroupsServicesConnector(), mtm, gm, gmm);
230
231 UUID userId = TestHelpers.ParseTail(0x1);
232 string subjectText = "newman";
233 string messageText = "Hello";
234 string combinedSubjectMessage = string.Format("{0}|{1}", subjectText, messageText);
235
236 ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x1));
237 TestClient tc = (TestClient)sp.ControllingClient;
238
239 UUID groupID = gm.CreateGroup(tc, "group1", null, true, UUID.Zero, 0, true, true, true);
240 gm.JoinGroupRequest(tc, groupID);
241
242 // Create a second user who doesn't want to receive notices
243 ScenePresence sp2 = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x2));
244 TestClient tc2 = (TestClient)sp2.ControllingClient;
245 gm.JoinGroupRequest(tc2, groupID);
246 gm.SetGroupAcceptNotices(tc2, groupID, false, true);
247
248 List<GridInstantMessage> spReceivedMessages = new List<GridInstantMessage>();
249 tc.OnReceivedInstantMessage += im => spReceivedMessages.Add(im);
250
251 List<GridInstantMessage> sp2ReceivedMessages = new List<GridInstantMessage>();
252 tc2.OnReceivedInstantMessage += im => sp2ReceivedMessages.Add(im);
253
254 GridInstantMessage noticeIm = new GridInstantMessage();
255 noticeIm.fromAgentID = userId.Guid;
256 noticeIm.toAgentID = groupID.Guid;
257 noticeIm.message = combinedSubjectMessage;
258 noticeIm.dialog = (byte)InstantMessageDialog.GroupNotice;
259
260 tc.HandleImprovedInstantMessage(noticeIm);
261
262 Assert.That(spReceivedMessages.Count, Is.EqualTo(1));
263 Assert.That(spReceivedMessages[0].message, Is.EqualTo(combinedSubjectMessage));
264
265 Assert.That(sp2ReceivedMessages.Count, Is.EqualTo(0));
266 }
267 }
268} \ No newline at end of file
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
new file mode 100644
index 0000000..a040f43
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
@@ -0,0 +1,1174 @@
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;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Text;
33
34using Nwc.XmlRpc;
35
36using log4net;
37using Mono.Addins;
38using Nini.Config;
39
40using OpenMetaverse;
41using OpenMetaverse.StructuredData;
42
43using OpenSim.Framework;
44using OpenSim.Framework.Communications;
45using OpenSim.Region.Framework.Interfaces;
46using OpenSim.Services.Interfaces;
47
48namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
49{
50 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XmlRpcGroupsServicesConnectorModule")]
51 public class XmlRpcGroupsServicesConnectorModule : ISharedRegionModule, IGroupsServicesConnector
52 {
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54
55 private bool m_debugEnabled = false;
56
57 public const GroupPowers DefaultEveryonePowers
58 = GroupPowers.AllowSetHome
59 | GroupPowers.Accountable
60 | GroupPowers.JoinChat
61 | GroupPowers.AllowVoiceChat
62 | GroupPowers.ReceiveNotices
63 | GroupPowers.StartProposal
64 | GroupPowers.VoteOnProposal;
65
66 // Would this be cleaner as (GroupPowers)ulong.MaxValue?
67 public const GroupPowers DefaultOwnerPowers
68 = GroupPowers.Accountable
69 | GroupPowers.AllowEditLand
70 | GroupPowers.AllowFly
71 | GroupPowers.AllowLandmark
72 | GroupPowers.AllowRez
73 | GroupPowers.AllowSetHome
74 | GroupPowers.AllowVoiceChat
75 | GroupPowers.AssignMember
76 | GroupPowers.AssignMemberLimited
77 | GroupPowers.ChangeActions
78 | GroupPowers.ChangeIdentity
79 | GroupPowers.ChangeMedia
80 | GroupPowers.ChangeOptions
81 | GroupPowers.CreateRole
82 | GroupPowers.DeedObject
83 | GroupPowers.DeleteRole
84 | GroupPowers.Eject
85 | GroupPowers.FindPlaces
86 | GroupPowers.Invite
87 | GroupPowers.JoinChat
88 | GroupPowers.LandChangeIdentity
89 | GroupPowers.LandDeed
90 | GroupPowers.LandDivideJoin
91 | GroupPowers.LandEdit
92 | GroupPowers.LandEjectAndFreeze
93 | GroupPowers.LandGardening
94 | GroupPowers.LandManageAllowed
95 | GroupPowers.LandManageBanned
96 | GroupPowers.LandManagePasses
97 | GroupPowers.LandOptions
98 | GroupPowers.LandRelease
99 | GroupPowers.LandSetSale
100 | GroupPowers.ModerateChat
101 | GroupPowers.ObjectManipulate
102 | GroupPowers.ObjectSetForSale
103 | GroupPowers.ReceiveNotices
104 | GroupPowers.RemoveMember
105 | GroupPowers.ReturnGroupOwned
106 | GroupPowers.ReturnGroupSet
107 | GroupPowers.ReturnNonGroup
108 | GroupPowers.RoleProperties
109 | GroupPowers.SendNotices
110 | GroupPowers.SetLandingPoint
111 | GroupPowers.StartProposal
112 | GroupPowers.VoteOnProposal;
113
114 private bool m_connectorEnabled = false;
115
116 private string m_groupsServerURI = string.Empty;
117
118 private bool m_disableKeepAlive = false;
119
120 private string m_groupReadKey = string.Empty;
121 private string m_groupWriteKey = string.Empty;
122
123 private IUserAccountService m_accountService = null;
124
125 private ExpiringCache<string, XmlRpcResponse> m_memoryCache;
126 private int m_cacheTimeout = 30;
127
128 // Used to track which agents are have dropped from a group chat session
129 // Should be reset per agent, on logon
130 // TODO: move this to Flotsam XmlRpc Service
131 // SessionID, List<AgentID>
132 private Dictionary<UUID, List<UUID>> m_groupsAgentsDroppedFromChatSession = new Dictionary<UUID, List<UUID>>();
133 private Dictionary<UUID, List<UUID>> m_groupsAgentsInvitedToChatSession = new Dictionary<UUID, List<UUID>>();
134
135 #region Region Module interfaceBase Members
136
137 public string Name
138 {
139 get { return "XmlRpcGroupsServicesConnector"; }
140 }
141
142 // this module is not intended to be replaced, but there should only be 1 of them.
143 public Type ReplaceableInterface
144 {
145 get { return null; }
146 }
147
148 public void Initialise(IConfigSource config)
149 {
150 IConfig groupsConfig = config.Configs["Groups"];
151
152 if (groupsConfig == null)
153 {
154 // Do not run this module by default.
155 return;
156 }
157 else
158 {
159 // if groups aren't enabled, we're not needed.
160 // if we're not specified as the connector to use, then we're not wanted
161 if ((groupsConfig.GetBoolean("Enabled", false) == false)
162 || (groupsConfig.GetString("ServicesConnectorModule", "XmlRpcGroupsServicesConnector") != Name))
163 {
164 m_connectorEnabled = false;
165 return;
166 }
167
168 m_log.DebugFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name);
169
170 m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty);
171 if (string.IsNullOrEmpty(m_groupsServerURI))
172 {
173 m_log.ErrorFormat("Please specify a valid URL for GroupsServerURI in OpenSim.ini, [Groups]");
174 m_connectorEnabled = false;
175 return;
176 }
177
178 m_disableKeepAlive = groupsConfig.GetBoolean("XmlRpcDisableKeepAlive", false);
179
180 m_groupReadKey = groupsConfig.GetString("XmlRpcServiceReadKey", string.Empty);
181 m_groupWriteKey = groupsConfig.GetString("XmlRpcServiceWriteKey", string.Empty);
182
183
184 m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30);
185 if (m_cacheTimeout == 0)
186 {
187 m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Disabled.");
188 }
189 else
190 {
191 m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Timeout set to {0}.", m_cacheTimeout);
192 }
193
194 m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false);
195
196 // If we got all the config options we need, lets start'er'up
197 m_memoryCache = new ExpiringCache<string, XmlRpcResponse>();
198 m_connectorEnabled = true;
199 }
200 }
201
202 public void Close()
203 {
204 m_log.DebugFormat("[XMLRPC-GROUPS-CONNECTOR]: Closing {0}", this.Name);
205 }
206
207 public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene)
208 {
209 if (m_connectorEnabled)
210 {
211
212 if (m_accountService == null)
213 {
214 m_accountService = scene.UserAccountService;
215 }
216
217
218 scene.RegisterModuleInterface<IGroupsServicesConnector>(this);
219 }
220 }
221
222 public void RemoveRegion(OpenSim.Region.Framework.Scenes.Scene scene)
223 {
224 if (scene.RequestModuleInterface<IGroupsServicesConnector>() == this)
225 {
226 scene.UnregisterModuleInterface<IGroupsServicesConnector>(this);
227 }
228 }
229
230 public void RegionLoaded(OpenSim.Region.Framework.Scenes.Scene scene)
231 {
232 // TODO: May want to consider listenning for Agent Connections so we can pre-cache group info
233 // scene.EventManager.OnNewClient += OnNewClient;
234 }
235
236 #endregion
237
238 #region ISharedRegionModule Members
239
240 public void PostInitialise()
241 {
242 // NoOp
243 }
244
245 #endregion
246
247 #region IGroupsServicesConnector Members
248
249 /// <summary>
250 /// Create a Group, including Everyone and Owners Role, place FounderID in both groups, select Owner as selected role, and newly created group as agent's active role.
251 /// </summary>
252 public UUID CreateGroup(UUID requestingAgentID, string name, string charter, bool showInList, UUID insigniaID,
253 int membershipFee, bool openEnrollment, bool allowPublish,
254 bool maturePublish, UUID founderID)
255 {
256 UUID GroupID = UUID.Random();
257 UUID OwnerRoleID = UUID.Random();
258
259 Hashtable param = new Hashtable();
260 param["GroupID"] = GroupID.ToString();
261 param["Name"] = name;
262 param["Charter"] = charter;
263 param["ShowInList"] = showInList == true ? 1 : 0;
264 param["InsigniaID"] = insigniaID.ToString();
265 param["MembershipFee"] = membershipFee;
266 param["OpenEnrollment"] = openEnrollment == true ? 1 : 0;
267 param["AllowPublish"] = allowPublish == true ? 1 : 0;
268 param["MaturePublish"] = maturePublish == true ? 1 : 0;
269 param["FounderID"] = founderID.ToString();
270 param["EveryonePowers"] = ((ulong)DefaultEveryonePowers).ToString();
271 param["OwnerRoleID"] = OwnerRoleID.ToString();
272 param["OwnersPowers"] = ((ulong)DefaultOwnerPowers).ToString();
273
274 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.createGroup", param);
275
276 if (respData.Contains("error"))
277 {
278 // UUID is not nullable
279
280 return UUID.Zero;
281 }
282
283 return UUID.Parse((string)respData["GroupID"]);
284 }
285
286 public void UpdateGroup(UUID requestingAgentID, UUID groupID, string charter, bool showInList,
287 UUID insigniaID, int membershipFee, bool openEnrollment,
288 bool allowPublish, bool maturePublish)
289 {
290 Hashtable param = new Hashtable();
291 param["GroupID"] = groupID.ToString();
292 param["Charter"] = charter;
293 param["ShowInList"] = showInList == true ? 1 : 0;
294 param["InsigniaID"] = insigniaID.ToString();
295 param["MembershipFee"] = membershipFee;
296 param["OpenEnrollment"] = openEnrollment == true ? 1 : 0;
297 param["AllowPublish"] = allowPublish == true ? 1 : 0;
298 param["MaturePublish"] = maturePublish == true ? 1 : 0;
299
300 XmlRpcCall(requestingAgentID, "groups.updateGroup", param);
301 }
302
303 public void AddGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description,
304 string title, ulong powers)
305 {
306 Hashtable param = new Hashtable();
307 param["GroupID"] = groupID.ToString();
308 param["RoleID"] = roleID.ToString();
309 param["Name"] = name;
310 param["Description"] = description;
311 param["Title"] = title;
312 param["Powers"] = powers.ToString();
313
314 XmlRpcCall(requestingAgentID, "groups.addRoleToGroup", param);
315 }
316
317 public void RemoveGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID)
318 {
319 Hashtable param = new Hashtable();
320 param["GroupID"] = groupID.ToString();
321 param["RoleID"] = roleID.ToString();
322
323 XmlRpcCall(requestingAgentID, "groups.removeRoleFromGroup", param);
324 }
325
326 public void UpdateGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description,
327 string title, ulong powers)
328 {
329 Hashtable param = new Hashtable();
330 param["GroupID"] = groupID.ToString();
331 param["RoleID"] = roleID.ToString();
332 if (name != null)
333 {
334 param["Name"] = name;
335 }
336 if (description != null)
337 {
338 param["Description"] = description;
339 }
340 if (title != null)
341 {
342 param["Title"] = title;
343 }
344 param["Powers"] = powers.ToString();
345
346 XmlRpcCall(requestingAgentID, "groups.updateGroupRole", param);
347 }
348
349 public GroupRecord GetGroupRecord(UUID requestingAgentID, UUID GroupID, string GroupName)
350 {
351 Hashtable param = new Hashtable();
352 if (GroupID != UUID.Zero)
353 {
354 param["GroupID"] = GroupID.ToString();
355 }
356 if (!string.IsNullOrEmpty(GroupName))
357 {
358 param["Name"] = GroupName.ToString();
359 }
360
361 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroup", param);
362
363 if (respData.Contains("error"))
364 {
365 return null;
366 }
367
368 return GroupProfileHashtableToGroupRecord(respData);
369
370 }
371
372 public GroupProfileData GetMemberGroupProfile(UUID requestingAgentID, UUID GroupID, UUID AgentID)
373 {
374 Hashtable param = new Hashtable();
375 param["GroupID"] = GroupID.ToString();
376
377 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroup", param);
378
379 if (respData.Contains("error"))
380 {
381 // GroupProfileData is not nullable
382 return new GroupProfileData();
383 }
384
385 GroupMembershipData MemberInfo = GetAgentGroupMembership(requestingAgentID, AgentID, GroupID);
386 GroupProfileData MemberGroupProfile = GroupProfileHashtableToGroupProfileData(respData);
387
388 MemberGroupProfile.MemberTitle = MemberInfo.GroupTitle;
389 MemberGroupProfile.PowersMask = MemberInfo.GroupPowers;
390
391 return MemberGroupProfile;
392 }
393
394 public void SetAgentActiveGroup(UUID requestingAgentID, UUID AgentID, UUID GroupID)
395 {
396 Hashtable param = new Hashtable();
397 param["AgentID"] = AgentID.ToString();
398 param["GroupID"] = GroupID.ToString();
399
400 XmlRpcCall(requestingAgentID, "groups.setAgentActiveGroup", param);
401 }
402
403 public void SetAgentActiveGroupRole(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID)
404 {
405 Hashtable param = new Hashtable();
406 param["AgentID"] = AgentID.ToString();
407 param["GroupID"] = GroupID.ToString();
408 param["SelectedRoleID"] = RoleID.ToString();
409
410 XmlRpcCall(requestingAgentID, "groups.setAgentGroupInfo", param);
411 }
412
413 public void SetAgentGroupInfo(UUID requestingAgentID, UUID AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile)
414 {
415 Hashtable param = new Hashtable();
416 param["AgentID"] = AgentID.ToString();
417 param["GroupID"] = GroupID.ToString();
418 param["AcceptNotices"] = AcceptNotices ? "1" : "0";
419 param["ListInProfile"] = ListInProfile ? "1" : "0";
420
421 XmlRpcCall(requestingAgentID, "groups.setAgentGroupInfo", param);
422
423 }
424
425 public void AddAgentToGroupInvite(UUID requestingAgentID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID)
426 {
427 Hashtable param = new Hashtable();
428 param["InviteID"] = inviteID.ToString();
429 param["AgentID"] = agentID.ToString();
430 param["RoleID"] = roleID.ToString();
431 param["GroupID"] = groupID.ToString();
432
433 XmlRpcCall(requestingAgentID, "groups.addAgentToGroupInvite", param);
434
435 }
436
437 public GroupInviteInfo GetAgentToGroupInvite(UUID requestingAgentID, UUID inviteID)
438 {
439 Hashtable param = new Hashtable();
440 param["InviteID"] = inviteID.ToString();
441
442 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getAgentToGroupInvite", param);
443
444 if (respData.Contains("error"))
445 {
446 return null;
447 }
448
449 GroupInviteInfo inviteInfo = new GroupInviteInfo();
450 inviteInfo.InviteID = inviteID;
451 inviteInfo.GroupID = UUID.Parse((string)respData["GroupID"]);
452 inviteInfo.RoleID = UUID.Parse((string)respData["RoleID"]);
453 inviteInfo.AgentID = UUID.Parse((string)respData["AgentID"]);
454
455 return inviteInfo;
456 }
457
458 public void RemoveAgentToGroupInvite(UUID requestingAgentID, UUID inviteID)
459 {
460 Hashtable param = new Hashtable();
461 param["InviteID"] = inviteID.ToString();
462
463 XmlRpcCall(requestingAgentID, "groups.removeAgentToGroupInvite", param);
464 }
465
466 public void AddAgentToGroup(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID)
467 {
468 Hashtable param = new Hashtable();
469 param["AgentID"] = AgentID.ToString();
470 param["GroupID"] = GroupID.ToString();
471 param["RoleID"] = RoleID.ToString();
472
473 XmlRpcCall(requestingAgentID, "groups.addAgentToGroup", param);
474 }
475
476 public void RemoveAgentFromGroup(UUID requestingAgentID, UUID AgentID, UUID GroupID)
477 {
478 Hashtable param = new Hashtable();
479 param["AgentID"] = AgentID.ToString();
480 param["GroupID"] = GroupID.ToString();
481
482 XmlRpcCall(requestingAgentID, "groups.removeAgentFromGroup", param);
483 }
484
485 public void AddAgentToGroupRole(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID)
486 {
487 Hashtable param = new Hashtable();
488 param["AgentID"] = AgentID.ToString();
489 param["GroupID"] = GroupID.ToString();
490 param["RoleID"] = RoleID.ToString();
491
492 XmlRpcCall(requestingAgentID, "groups.addAgentToGroupRole", param);
493 }
494
495 public void RemoveAgentFromGroupRole(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID)
496 {
497 Hashtable param = new Hashtable();
498 param["AgentID"] = AgentID.ToString();
499 param["GroupID"] = GroupID.ToString();
500 param["RoleID"] = RoleID.ToString();
501
502 XmlRpcCall(requestingAgentID, "groups.removeAgentFromGroupRole", param);
503 }
504
505 public List<DirGroupsReplyData> FindGroups(UUID requestingAgentID, string search)
506 {
507 Hashtable param = new Hashtable();
508 param["Search"] = search;
509
510 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.findGroups", param);
511
512 List<DirGroupsReplyData> findings = new List<DirGroupsReplyData>();
513
514 if (!respData.Contains("error"))
515 {
516 Hashtable results = (Hashtable)respData["results"];
517 foreach (Hashtable groupFind in results.Values)
518 {
519 DirGroupsReplyData data = new DirGroupsReplyData();
520 data.groupID = new UUID((string)groupFind["GroupID"]); ;
521 data.groupName = (string)groupFind["Name"];
522 data.members = int.Parse((string)groupFind["Members"]);
523 // data.searchOrder = order;
524
525 findings.Add(data);
526 }
527 }
528
529 return findings;
530 }
531
532 public GroupMembershipData GetAgentGroupMembership(UUID requestingAgentID, UUID AgentID, UUID GroupID)
533 {
534 Hashtable param = new Hashtable();
535 param["AgentID"] = AgentID.ToString();
536 param["GroupID"] = GroupID.ToString();
537
538 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getAgentGroupMembership", param);
539
540 if (respData.Contains("error"))
541 {
542 return null;
543 }
544
545 GroupMembershipData data = HashTableToGroupMembershipData(respData);
546
547 return data;
548 }
549
550 public GroupMembershipData GetAgentActiveMembership(UUID requestingAgentID, UUID AgentID)
551 {
552 Hashtable param = new Hashtable();
553 param["AgentID"] = AgentID.ToString();
554
555 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getAgentActiveMembership", param);
556
557 if (respData.Contains("error"))
558 {
559 return null;
560 }
561
562 return HashTableToGroupMembershipData(respData);
563 }
564
565 public List<GroupMembershipData> GetAgentGroupMemberships(UUID requestingAgentID, UUID AgentID)
566 {
567 Hashtable param = new Hashtable();
568 param["AgentID"] = AgentID.ToString();
569
570 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getAgentGroupMemberships", param);
571
572 List<GroupMembershipData> memberships = new List<GroupMembershipData>();
573
574 if (!respData.Contains("error"))
575 {
576 foreach (object membership in respData.Values)
577 {
578 memberships.Add(HashTableToGroupMembershipData((Hashtable)membership));
579 }
580 }
581
582 return memberships;
583 }
584
585 public List<GroupRolesData> GetAgentGroupRoles(UUID requestingAgentID, UUID AgentID, UUID GroupID)
586 {
587 Hashtable param = new Hashtable();
588 param["AgentID"] = AgentID.ToString();
589 param["GroupID"] = GroupID.ToString();
590
591 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getAgentRoles", param);
592
593 List<GroupRolesData> Roles = new List<GroupRolesData>();
594
595 if (respData.Contains("error"))
596 {
597 return Roles;
598 }
599
600 foreach (Hashtable role in respData.Values)
601 {
602 GroupRolesData data = new GroupRolesData();
603 data.RoleID = new UUID((string)role["RoleID"]);
604 data.Name = (string)role["Name"];
605 data.Description = (string)role["Description"];
606 data.Powers = ulong.Parse((string)role["Powers"]);
607 data.Title = (string)role["Title"];
608
609 Roles.Add(data);
610 }
611
612 return Roles;
613 }
614
615 public List<GroupRolesData> GetGroupRoles(UUID requestingAgentID, UUID GroupID)
616 {
617 Hashtable param = new Hashtable();
618 param["GroupID"] = GroupID.ToString();
619
620 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupRoles", param);
621
622 List<GroupRolesData> Roles = new List<GroupRolesData>();
623
624 if (respData.Contains("error"))
625 {
626 return Roles;
627 }
628
629 foreach (Hashtable role in respData.Values)
630 {
631 GroupRolesData data = new GroupRolesData();
632 data.Description = (string)role["Description"];
633 data.Members = int.Parse((string)role["Members"]);
634 data.Name = (string)role["Name"];
635 data.Powers = ulong.Parse((string)role["Powers"]);
636 data.RoleID = new UUID((string)role["RoleID"]);
637 data.Title = (string)role["Title"];
638
639 Roles.Add(data);
640 }
641
642 return Roles;
643 }
644
645 public List<GroupMembersData> GetGroupMembers(UUID requestingAgentID, UUID GroupID)
646 {
647 Hashtable param = new Hashtable();
648 param["GroupID"] = GroupID.ToString();
649
650 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupMembers", param);
651
652 List<GroupMembersData> members = new List<GroupMembersData>();
653
654 if (respData.Contains("error"))
655 {
656 return members;
657 }
658
659 foreach (Hashtable membership in respData.Values)
660 {
661 GroupMembersData data = new GroupMembersData();
662
663 data.AcceptNotices = ((string)membership["AcceptNotices"]) == "1";
664 data.AgentID = new UUID((string)membership["AgentID"]);
665 data.Contribution = int.Parse((string)membership["Contribution"]);
666 data.IsOwner = ((string)membership["IsOwner"]) == "1";
667 data.ListInProfile = ((string)membership["ListInProfile"]) == "1";
668 data.AgentPowers = ulong.Parse((string)membership["AgentPowers"]);
669 data.Title = (string)membership["Title"];
670
671 members.Add(data);
672 }
673
674 return members;
675 }
676
677 public List<GroupRoleMembersData> GetGroupRoleMembers(UUID requestingAgentID, UUID GroupID)
678 {
679 Hashtable param = new Hashtable();
680 param["GroupID"] = GroupID.ToString();
681
682 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupRoleMembers", param);
683
684 List<GroupRoleMembersData> members = new List<GroupRoleMembersData>();
685
686 if (!respData.Contains("error"))
687 {
688 foreach (Hashtable membership in respData.Values)
689 {
690 GroupRoleMembersData data = new GroupRoleMembersData();
691
692 data.MemberID = new UUID((string)membership["AgentID"]);
693 data.RoleID = new UUID((string)membership["RoleID"]);
694
695 members.Add(data);
696 }
697 }
698 return members;
699 }
700
701 public List<GroupNoticeData> GetGroupNotices(UUID requestingAgentID, UUID GroupID)
702 {
703 Hashtable param = new Hashtable();
704 param["GroupID"] = GroupID.ToString();
705
706 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupNotices", param);
707
708 List<GroupNoticeData> values = new List<GroupNoticeData>();
709
710 if (!respData.Contains("error"))
711 {
712 foreach (Hashtable value in respData.Values)
713 {
714 GroupNoticeData data = new GroupNoticeData();
715 data.NoticeID = UUID.Parse((string)value["NoticeID"]);
716 data.Timestamp = uint.Parse((string)value["Timestamp"]);
717 data.FromName = (string)value["FromName"];
718 data.Subject = (string)value["Subject"];
719 data.HasAttachment = false;
720 data.AssetType = 0;
721
722 values.Add(data);
723 }
724 }
725
726 return values;
727 }
728
729 public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID)
730 {
731 Hashtable param = new Hashtable();
732 param["NoticeID"] = noticeID.ToString();
733
734 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupNotice", param);
735
736 if (respData.Contains("error"))
737 {
738 return null;
739 }
740
741 GroupNoticeInfo data = new GroupNoticeInfo();
742 data.GroupID = UUID.Parse((string)respData["GroupID"]);
743 data.Message = (string)respData["Message"];
744 data.BinaryBucket = Utils.HexStringToBytes((string)respData["BinaryBucket"], true);
745 data.noticeData.NoticeID = UUID.Parse((string)respData["NoticeID"]);
746 data.noticeData.Timestamp = uint.Parse((string)respData["Timestamp"]);
747 data.noticeData.FromName = (string)respData["FromName"];
748 data.noticeData.Subject = (string)respData["Subject"];
749 data.noticeData.HasAttachment = false;
750 data.noticeData.AssetType = 0;
751
752 if (data.Message == null)
753 {
754 data.Message = string.Empty;
755 }
756
757 return data;
758 }
759
760 public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket)
761 {
762 string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, "");
763
764 Hashtable param = new Hashtable();
765 param["GroupID"] = groupID.ToString();
766 param["NoticeID"] = noticeID.ToString();
767 param["FromName"] = fromName;
768 param["Subject"] = subject;
769 param["Message"] = message;
770 param["BinaryBucket"] = binBucket;
771 param["TimeStamp"] = ((uint)Util.UnixTimeSinceEpoch()).ToString();
772
773 XmlRpcCall(requestingAgentID, "groups.addGroupNotice", param);
774 }
775
776 #endregion
777
778 #region GroupSessionTracking
779
780 public void ResetAgentGroupChatSessions(UUID agentID)
781 {
782 foreach (List<UUID> agentList in m_groupsAgentsDroppedFromChatSession.Values)
783 {
784 agentList.Remove(agentID);
785 }
786 }
787
788 public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID)
789 {
790 // If we're tracking this group, and we can find them in the tracking, then they've been invited
791 return m_groupsAgentsInvitedToChatSession.ContainsKey(groupID)
792 && m_groupsAgentsInvitedToChatSession[groupID].Contains(agentID);
793 }
794
795 public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID)
796 {
797 // If we're tracking drops for this group,
798 // and we find them, well... then they've dropped
799 return m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID)
800 && m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID);
801 }
802
803 public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID)
804 {
805 if (m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID))
806 {
807 // If not in dropped list, add
808 if (!m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID))
809 {
810 m_groupsAgentsDroppedFromChatSession[groupID].Add(agentID);
811 }
812 }
813 }
814
815 public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID)
816 {
817 // Add Session Status if it doesn't exist for this session
818 CreateGroupChatSessionTracking(groupID);
819
820 // If nessesary, remove from dropped list
821 if (m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID))
822 {
823 m_groupsAgentsDroppedFromChatSession[groupID].Remove(agentID);
824 }
825 }
826
827 private void CreateGroupChatSessionTracking(UUID groupID)
828 {
829 if (!m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID))
830 {
831 m_groupsAgentsDroppedFromChatSession.Add(groupID, new List<UUID>());
832 m_groupsAgentsInvitedToChatSession.Add(groupID, new List<UUID>());
833 }
834
835 }
836 #endregion
837
838 #region XmlRpcHashtableMarshalling
839 private GroupProfileData GroupProfileHashtableToGroupProfileData(Hashtable groupProfile)
840 {
841 GroupProfileData group = new GroupProfileData();
842 group.GroupID = UUID.Parse((string)groupProfile["GroupID"]);
843 group.Name = (string)groupProfile["Name"];
844
845 if (groupProfile["Charter"] != null)
846 {
847 group.Charter = (string)groupProfile["Charter"];
848 }
849
850 group.ShowInList = ((string)groupProfile["ShowInList"]) == "1";
851 group.InsigniaID = UUID.Parse((string)groupProfile["InsigniaID"]);
852 group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]);
853 group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1";
854 group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1";
855 group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1";
856 group.FounderID = UUID.Parse((string)groupProfile["FounderID"]);
857 group.OwnerRole = UUID.Parse((string)groupProfile["OwnerRoleID"]);
858
859 group.GroupMembershipCount = int.Parse((string)groupProfile["GroupMembershipCount"]);
860 group.GroupRolesCount = int.Parse((string)groupProfile["GroupRolesCount"]);
861
862 return group;
863 }
864
865 private GroupRecord GroupProfileHashtableToGroupRecord(Hashtable groupProfile)
866 {
867 GroupRecord group = new GroupRecord();
868 group.GroupID = UUID.Parse((string)groupProfile["GroupID"]);
869 group.GroupName = groupProfile["Name"].ToString();
870 if (groupProfile["Charter"] != null)
871 {
872 group.Charter = (string)groupProfile["Charter"];
873 }
874 group.ShowInList = ((string)groupProfile["ShowInList"]) == "1";
875 group.GroupPicture = UUID.Parse((string)groupProfile["InsigniaID"]);
876 group.MembershipFee = int.Parse((string)groupProfile["MembershipFee"]);
877 group.OpenEnrollment = ((string)groupProfile["OpenEnrollment"]) == "1";
878 group.AllowPublish = ((string)groupProfile["AllowPublish"]) == "1";
879 group.MaturePublish = ((string)groupProfile["MaturePublish"]) == "1";
880 group.FounderID = UUID.Parse((string)groupProfile["FounderID"]);
881 group.OwnerRoleID = UUID.Parse((string)groupProfile["OwnerRoleID"]);
882
883 return group;
884 }
885
886 private static GroupMembershipData HashTableToGroupMembershipData(Hashtable respData)
887 {
888 GroupMembershipData data = new GroupMembershipData();
889 data.AcceptNotices = ((string)respData["AcceptNotices"] == "1");
890 data.Contribution = int.Parse((string)respData["Contribution"]);
891 data.ListInProfile = ((string)respData["ListInProfile"] == "1");
892
893 data.ActiveRole = new UUID((string)respData["SelectedRoleID"]);
894 data.GroupTitle = (string)respData["Title"];
895
896 data.GroupPowers = ulong.Parse((string)respData["GroupPowers"]);
897
898 // Is this group the agent's active group
899
900 data.GroupID = new UUID((string)respData["GroupID"]);
901
902 UUID ActiveGroup = new UUID((string)respData["ActiveGroupID"]);
903 data.Active = data.GroupID.Equals(ActiveGroup);
904
905 data.AllowPublish = ((string)respData["AllowPublish"] == "1");
906 if (respData["Charter"] != null)
907 {
908 data.Charter = (string)respData["Charter"];
909 }
910 data.FounderID = new UUID((string)respData["FounderID"]);
911 data.GroupID = new UUID((string)respData["GroupID"]);
912 data.GroupName = (string)respData["GroupName"];
913 data.GroupPicture = new UUID((string)respData["InsigniaID"]);
914 data.MaturePublish = ((string)respData["MaturePublish"] == "1");
915 data.MembershipFee = int.Parse((string)respData["MembershipFee"]);
916 data.OpenEnrollment = ((string)respData["OpenEnrollment"] == "1");
917 data.ShowInList = ((string)respData["ShowInList"] == "1");
918
919 return data;
920 }
921
922 #endregion
923
924 /// <summary>
925 /// Encapsulate the XmlRpc call to standardize security and error handling.
926 /// </summary>
927 private Hashtable XmlRpcCall(UUID requestingAgentID, string function, Hashtable param)
928 {
929 XmlRpcResponse resp = null;
930 string CacheKey = null;
931
932 // Only bother with the cache if it isn't disabled.
933 if (m_cacheTimeout > 0)
934 {
935 if (!function.StartsWith("groups.get"))
936 {
937 // Any and all updates cause the cache to clear
938 m_memoryCache.Clear();
939 }
940 else
941 {
942 StringBuilder sb = new StringBuilder(requestingAgentID + function);
943 foreach (object key in param.Keys)
944 {
945 if (param[key] != null)
946 {
947 sb.AppendFormat(",{0}:{1}", key.ToString(), param[key].ToString());
948 }
949 }
950
951 CacheKey = sb.ToString();
952 m_memoryCache.TryGetValue(CacheKey, out resp);
953 }
954 }
955
956 if (resp == null)
957 {
958 if (m_debugEnabled)
959 m_log.DebugFormat("[XMLRPC-GROUPS-CONNECTOR]: Cache miss for key {0}", CacheKey);
960
961 string UserService;
962 UUID SessionID;
963 GetClientGroupRequestID(requestingAgentID, out UserService, out SessionID);
964
965 param.Add("RequestingAgentID", requestingAgentID.ToString());
966 param.Add("RequestingAgentUserService", UserService);
967 param.Add("RequestingSessionID", SessionID.ToString());
968 param.Add("ReadKey", m_groupReadKey);
969 param.Add("WriteKey", m_groupWriteKey);
970
971 IList parameters = new ArrayList();
972 parameters.Add(param);
973
974 ConfigurableKeepAliveXmlRpcRequest req;
975 req = new ConfigurableKeepAliveXmlRpcRequest(function, parameters, m_disableKeepAlive);
976
977 try
978 {
979 resp = req.Send(m_groupsServerURI, 10000);
980
981 if ((m_cacheTimeout > 0) && (CacheKey != null))
982 {
983 m_memoryCache.AddOrUpdate(CacheKey, resp, TimeSpan.FromSeconds(m_cacheTimeout));
984 }
985 }
986 catch (Exception e)
987 {
988 m_log.ErrorFormat(
989 "[XMLRPC-GROUPS-CONNECTOR]: An error has occured while attempting to access the XmlRpcGroups server method {0} at {1}",
990 function, m_groupsServerURI);
991
992 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0}{1}", e.Message, e.StackTrace);
993
994 foreach (string ResponseLine in req.RequestResponse.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
995 {
996 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} ", ResponseLine);
997 }
998
999 foreach (string key in param.Keys)
1000 {
1001 m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", key, param[key].ToString());
1002 }
1003
1004 Hashtable respData = new Hashtable();
1005 respData.Add("error", e.ToString());
1006 return respData;
1007 }
1008 }
1009
1010 if (resp.Value is Hashtable)
1011 {
1012 Hashtable respData = (Hashtable)resp.Value;
1013 if (respData.Contains("error") && !respData.Contains("succeed"))
1014 {
1015 LogRespDataToConsoleError(requestingAgentID, function, param, respData);
1016 }
1017
1018 return respData;
1019 }
1020
1021 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString());
1022
1023 if (resp.Value is ArrayList)
1024 {
1025 ArrayList al = (ArrayList)resp.Value;
1026 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Contains {0} elements", al.Count);
1027
1028 foreach (object o in al)
1029 {
1030 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", o.GetType().ToString(), o.ToString());
1031 }
1032 }
1033 else
1034 {
1035 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Function returned: {0}", resp.Value.ToString());
1036 }
1037
1038 Hashtable error = new Hashtable();
1039 error.Add("error", "invalid return value");
1040 return error;
1041 }
1042
1043 private void LogRespDataToConsoleError(UUID requestingAgentID, string function, Hashtable param, Hashtable respData)
1044 {
1045 m_log.ErrorFormat(
1046 "[XMLRPC-GROUPS-CONNECTOR]: Error when calling {0} for {1} with params {2}. Response params are {3}",
1047 function, requestingAgentID, Util.PrettyFormatToSingleLine(param), Util.PrettyFormatToSingleLine(respData));
1048 }
1049
1050 /// <summary>
1051 /// Group Request Tokens are an attempt to allow the groups service to authenticate
1052 /// requests.
1053 /// TODO: This broke after the big grid refactor, either find a better way, or discard this
1054 /// </summary>
1055 /// <param name="client"></param>
1056 /// <returns></returns>
1057 private void GetClientGroupRequestID(UUID AgentID, out string UserServiceURL, out UUID SessionID)
1058 {
1059 UserServiceURL = "";
1060 SessionID = UUID.Zero;
1061
1062
1063 // Need to rework this based on changes to User Services
1064 /*
1065 UserAccount userAccount = m_accountService.GetUserAccount(UUID.Zero,AgentID);
1066 if (userAccount == null)
1067 {
1068 // This should be impossible. If I've been passed a reference to a client
1069 // that client should be registered with the UserService. So something
1070 // is horribly wrong somewhere.
1071
1072 m_log.WarnFormat("[GROUPS]: Could not find a UserServiceURL for {0}", AgentID);
1073
1074 }
1075 else if (userProfile is ForeignUserProfileData)
1076 {
1077 // They aren't from around here
1078 ForeignUserProfileData fupd = (ForeignUserProfileData)userProfile;
1079 UserServiceURL = fupd.UserServerURI;
1080 SessionID = fupd.CurrentAgent.SessionID;
1081
1082 }
1083 else
1084 {
1085 // They're a local user, use this:
1086 UserServiceURL = m_commManager.NetworkServersInfo.UserURL;
1087 SessionID = userProfile.CurrentAgent.SessionID;
1088 }
1089 */
1090 }
1091
1092 }
1093}
1094
1095namespace Nwc.XmlRpc
1096{
1097 using System;
1098 using System.Collections;
1099 using System.IO;
1100 using System.Xml;
1101 using System.Net;
1102 using System.Text;
1103 using System.Reflection;
1104
1105 /// <summary>Class supporting the request side of an XML-RPC transaction.</summary>
1106 public class ConfigurableKeepAliveXmlRpcRequest : XmlRpcRequest
1107 {
1108 private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer();
1109 private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer();
1110 private bool _disableKeepAlive = true;
1111
1112 public string RequestResponse = String.Empty;
1113
1114 /// <summary>Instantiate an <c>XmlRpcRequest</c> for a specified method and parameters.</summary>
1115 /// <param name="methodName"><c>String</c> designating the <i>object.method</i> on the server the request
1116 /// should be directed to.</param>
1117 /// <param name="parameters"><c>ArrayList</c> of XML-RPC type parameters to invoke the request with.</param>
1118 public ConfigurableKeepAliveXmlRpcRequest(String methodName, IList parameters, bool disableKeepAlive)
1119 {
1120 MethodName = methodName;
1121 _params = parameters;
1122 _disableKeepAlive = disableKeepAlive;
1123 }
1124
1125 /// <summary>Send the request to the server.</summary>
1126 /// <param name="url"><c>String</c> The url of the XML-RPC server.</param>
1127 /// <returns><c>XmlRpcResponse</c> The response generated.</returns>
1128 public XmlRpcResponse Send(String url)
1129 {
1130 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
1131 if (request == null)
1132 throw new XmlRpcException(XmlRpcErrorCodes.TRANSPORT_ERROR,
1133 XmlRpcErrorCodes.TRANSPORT_ERROR_MSG + ": Could not create request with " + url);
1134 request.Method = "POST";
1135 request.ContentType = "text/xml";
1136 request.AllowWriteStreamBuffering = true;
1137 request.KeepAlive = !_disableKeepAlive;
1138
1139 using (Stream stream = request.GetRequestStream())
1140 {
1141 using (XmlTextWriter xml = new XmlTextWriter(stream, Encoding.ASCII))
1142 {
1143 _serializer.Serialize(xml, this);
1144 xml.Flush();
1145 }
1146 }
1147
1148 XmlRpcResponse resp;
1149
1150 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
1151 {
1152 using (Stream s = response.GetResponseStream())
1153 {
1154 using (StreamReader input = new StreamReader(s))
1155 {
1156 string inputXml = input.ReadToEnd();
1157
1158 try
1159 {
1160 resp = (XmlRpcResponse)_deserializer.Deserialize(inputXml);
1161 }
1162 catch (Exception e)
1163 {
1164 RequestResponse = inputXml;
1165 throw e;
1166 }
1167 }
1168 }
1169 }
1170
1171 return resp;
1172 }
1173 }
1174}