aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups')
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs289
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs272
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs6
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs214
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs71
5 files changed, 621 insertions, 231 deletions
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
index 2802e2f..e1b6abb 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs
@@ -55,8 +55,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
55 private IGroupsServicesConnector m_groupData = null; 55 private IGroupsServicesConnector m_groupData = null;
56 56
57 // Config Options 57 // Config Options
58 private bool m_groupMessagingEnabled = false; 58 private bool m_groupMessagingEnabled;
59 private bool m_debugEnabled = true; 59 private bool m_debugEnabled;
60 60
61 /// <summary> 61 /// <summary>
62 /// If enabled, module only tries to send group IMs to online users by querying cached presence information. 62 /// If enabled, module only tries to send group IMs to online users by querying cached presence information.
@@ -113,7 +113,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
113 if (m_messageOnlineAgentsOnly) 113 if (m_messageOnlineAgentsOnly)
114 m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>(); 114 m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
115 115
116 m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", true); 116 m_debugEnabled = groupsConfig.GetBoolean("MessagingDebugEnabled", m_debugEnabled);
117 } 117 }
118 118
119 m_log.InfoFormat( 119 m_log.InfoFormat(
@@ -127,6 +127,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
127 return; 127 return;
128 128
129 scene.RegisterModuleInterface<IGroupsMessagingModule>(this); 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);
130 } 138 }
131 139
132 public void RegionLoaded(Scene scene) 140 public void RegionLoaded(Scene scene)
@@ -218,6 +226,26 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
218 226
219 #endregion 227 #endregion
220 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
221 /// <summary> 249 /// <summary>
222 /// Not really needed, but does confirm that the group exists. 250 /// Not really needed, but does confirm that the group exists.
223 /// </summary> 251 /// </summary>
@@ -237,11 +265,20 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
237 return false; 265 return false;
238 } 266 }
239 } 267 }
240 268
241 public void SendMessageToGroup(GridInstantMessage im, UUID groupID) 269 public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
242 { 270 {
243 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(new UUID(im.fromAgentID), groupID); 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);
244 int groupMembersCount = groupMembers.Count; 280 int groupMembersCount = groupMembers.Count;
281 HashSet<string> attemptDeliveryUuidSet = null;
245 282
246 if (m_messageOnlineAgentsOnly) 283 if (m_messageOnlineAgentsOnly)
247 { 284 {
@@ -257,10 +294,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
257 m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds); 294 m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
258 } 295 }
259 296
260 HashSet<string> onlineAgentsUuidSet = new HashSet<string>(); 297 attemptDeliveryUuidSet
261 Array.ForEach<PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID)); 298 = new HashSet<string>(Array.ConvertAll<PresenceInfo, string>(onlineAgents, pi => pi.UserID));
299
300 //Array.ForEach<PresenceInfo>(onlineAgents, pi => attemptDeliveryUuidSet.Add(pi.UserID));
262 301
263 groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList(); 302 //groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
264 303
265 // if (m_debugEnabled) 304 // if (m_debugEnabled)
266// m_log.DebugFormat( 305// m_log.DebugFormat(
@@ -269,26 +308,42 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
269 } 308 }
270 else 309 else
271 { 310 {
311 attemptDeliveryUuidSet
312 = new HashSet<string>(groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()));
313
272 if (m_debugEnabled) 314 if (m_debugEnabled)
273 m_log.DebugFormat( 315 m_log.DebugFormat(
274 "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members", 316 "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
275 groupID, groupMembers.Count); 317 groupID, groupMembers.Count);
276 } 318 }
277
278 int requestStartTick = Environment.TickCount;
279 319
280 foreach (GroupMembersData member in groupMembers) 320 foreach (GroupMembersData member in groupMembers)
281 { 321 {
282 if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID)) 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))
283 { 335 {
284 // Don't deliver messages to people who have dropped this session 336 // Don't deliver messages to people who have dropped this session
285 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); 337 if (m_debugEnabled)
338 m_log.DebugFormat(
339 "[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID);
340
286 continue; 341 continue;
287 } 342 }
288 343
289 // Copy Message 344 // Copy Message
290 GridInstantMessage msg = new GridInstantMessage(); 345 GridInstantMessage msg = new GridInstantMessage();
291 msg.imSessionID = groupID.Guid; 346 msg.imSessionID = im.imSessionID;
292 msg.fromAgentName = im.fromAgentName; 347 msg.fromAgentName = im.fromAgentName;
293 msg.message = im.message; 348 msg.message = im.message;
294 msg.dialog = im.dialog; 349 msg.dialog = im.dialog;
@@ -304,26 +359,51 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
304 359
305 msg.toAgentID = member.AgentID.Guid; 360 msg.toAgentID = member.AgentID.Guid;
306 361
307 IClientAPI client = GetActiveClient(member.AgentID); 362 if (attemptDeliveryUuidSet.Contains(member.AgentID.ToString()))
308 if (client == null)
309 { 363 {
310 // If they're not local, forward across the grid 364 IClientAPI client = GetActiveClient(member.AgentID);
311 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); 365 if (client == null)
312 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); 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 }
313 } 389 }
314 else 390 else
315 { 391 {
316 // Deliver locally, directly 392 int startTick = Environment.TickCount;
317 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); 393
318 ProcessMessageFromGroupSession(msg); 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);
319 } 400 }
320 } 401 }
321 402
322 // Temporary for assessing how long it still takes to send messages to large online groups. 403 if (m_debugEnabled)
323 if (m_messageOnlineAgentsOnly)
324 m_log.DebugFormat( 404 m_log.DebugFormat(
325 "[GROUPS-MESSAGING]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms", 405 "[GROUPS-MESSAGING]: Total SendMessageToGroup for group {0} with {1} members, {2} candidates for delivery took {3} ms",
326 groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick); 406 groupID, groupMembersCount, attemptDeliveryUuidSet.Count(), Environment.TickCount - requestStartTick);
327 } 407 }
328 408
329 #region SimGridEventHandlers 409 #region SimGridEventHandlers
@@ -348,7 +428,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
348 // Any other message type will not be delivered to a client by the 428 // Any other message type will not be delivered to a client by the
349 // Instant Message Module 429 // Instant Message Module
350 430
351
352 if (m_debugEnabled) 431 if (m_debugEnabled)
353 { 432 {
354 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 433 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
@@ -362,13 +441,35 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
362 || (msg.dialog == (byte)InstantMessageDialog.SessionAdd) 441 || (msg.dialog == (byte)InstantMessageDialog.SessionAdd)
363 || (msg.dialog == (byte)InstantMessageDialog.SessionDrop))) 442 || (msg.dialog == (byte)InstantMessageDialog.SessionDrop)))
364 { 443 {
365 ProcessMessageFromGroupSession(msg); 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);
366 } 464 }
367 } 465 }
368 466
369 private void ProcessMessageFromGroupSession(GridInstantMessage msg) 467 private void ProcessMessageFromGroupSession(GridInstantMessage msg, IClientAPI client)
370 { 468 {
371 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID); 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);
372 473
373 UUID AgentID = new UUID(msg.fromAgentID); 474 UUID AgentID = new UUID(msg.fromAgentID);
374 UUID GroupID = new UUID(msg.imSessionID); 475 UUID GroupID = new UUID(msg.imSessionID);
@@ -392,74 +493,62 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
392 // Add them to the session for now, and Invite them 493 // Add them to the session for now, and Invite them
393 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); 494 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
394 495
395 UUID toAgentID = new UUID(msg.toAgentID); 496 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
396 IClientAPI activeClient = GetActiveClient(toAgentID); 497 if (groupInfo != null)
397 if (activeClient != null)
398 { 498 {
399 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null); 499 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message");
400 if (groupInfo != null) 500
401 { 501 // Force? open the group session dialog???
402 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message"); 502 // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg);
403 503 IEventQueue eq = client.Scene.RequestModuleInterface<IEventQueue>();
404 // Force? open the group session dialog??? 504 eq.ChatterboxInvitation(
405 // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg); 505 GroupID
406 IEventQueue eq = activeClient.Scene.RequestModuleInterface<IEventQueue>(); 506 , groupInfo.GroupName
407 eq.ChatterboxInvitation( 507 , new UUID(msg.fromAgentID)
408 GroupID 508 , msg.message
409 , groupInfo.GroupName 509 , new UUID(msg.toAgentID)
410 , new UUID(msg.fromAgentID) 510 , msg.fromAgentName
411 , msg.message 511 , msg.dialog
412 , new UUID(msg.toAgentID) 512 , msg.timestamp
413 , msg.fromAgentName 513 , msg.offline == 1
414 , msg.dialog 514 , (int)msg.ParentEstateID
415 , msg.timestamp 515 , msg.Position
416 , msg.offline == 1 516 , 1
417 , (int)msg.ParentEstateID 517 , new UUID(msg.imSessionID)
418 , msg.Position 518 , msg.fromGroup
419 , 1 519 , Utils.StringToBytes(groupInfo.GroupName)
420 , new UUID(msg.imSessionID) 520 );
421 , msg.fromGroup 521
422 , Utils.StringToBytes(groupInfo.GroupName) 522 eq.ChatterBoxSessionAgentListUpdates(
423 ); 523 new UUID(GroupID)
424 524 , new UUID(msg.fromAgentID)
425 eq.ChatterBoxSessionAgentListUpdates( 525 , new UUID(msg.toAgentID)
426 new UUID(GroupID) 526 , false //canVoiceChat
427 , new UUID(msg.fromAgentID) 527 , false //isModerator
428 , new UUID(msg.toAgentID) 528 , false //text mute
429 , false //canVoiceChat 529 );
430 , false //isModerator
431 , false //text mute
432 );
433 }
434 } 530 }
531
532 break;
435 } 533 }
436 else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID)) 534 else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID))
437 { 535 {
438 // User hasn't dropped, so they're in the session, 536 // User hasn't dropped, so they're in the session,
439 // maybe we should deliver it. 537 // maybe we should deliver it.
440 IClientAPI client = GetActiveClient(new UUID(msg.toAgentID)); 538 client.SendInstantMessage(msg);
441 if (client != null)
442 {
443 // Deliver locally, directly
444 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} locally", client.Name);
445 client.SendInstantMessage(msg);
446 }
447 else
448 {
449 m_log.WarnFormat("[GROUPS-MESSAGING]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
450 }
451 } 539 }
540
452 break; 541 break;
453 542
454 default: 543 default:
455 m_log.WarnFormat("[GROUPS-MESSAGING]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString()); 544 client.SendInstantMessage(msg);
456 break; 545
546 break;;
457 } 547 }
458 } 548 }
459 549
460 #endregion 550 #endregion
461 551
462
463 #region ClientEvents 552 #region ClientEvents
464 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) 553 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
465 { 554 {
@@ -548,15 +637,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
548 // Don't log any normal IMs (privacy!) 637 // Don't log any normal IMs (privacy!)
549 if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent) 638 if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent)
550 { 639 {
551 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromGroup({0})", im.fromGroup ? "True" : "False"); 640 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromGroup({0})", im.fromGroup ? "True" : "False");
552 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: Dialog({0})", ((InstantMessageDialog)im.dialog).ToString()); 641 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: Dialog({0})", (InstantMessageDialog)im.dialog);
553 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromAgentID({0})", im.fromAgentID.ToString()); 642 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromAgentID({0})", im.fromAgentID);
554 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: fromAgentName({0})", im.fromAgentName.ToString()); 643 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromAgentName({0})", im.fromAgentName);
555 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: imSessionID({0})", im.imSessionID.ToString()); 644 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: imSessionID({0})", im.imSessionID);
556 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: message({0})", im.message.ToString()); 645 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: message({0})", im.message);
557 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: offline({0})", im.offline.ToString()); 646 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: offline({0})", im.offline);
558 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: toAgentID({0})", im.toAgentID.ToString()); 647 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: toAgentID({0})", im.toAgentID);
559 m_log.WarnFormat("[GROUPS-MESSAGING]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket")); 648 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket"));
560 } 649 }
561 } 650 }
562 651
@@ -567,7 +656,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
567 /// </summary> 656 /// </summary>
568 private IClientAPI GetActiveClient(UUID agentID) 657 private IClientAPI GetActiveClient(UUID agentID)
569 { 658 {
570 if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Looking for local client {0}", agentID); 659 if (m_debugEnabled)
660 m_log.DebugFormat("[GROUPS-MESSAGING]: Looking for local client {0}", agentID);
571 661
572 IClientAPI child = null; 662 IClientAPI child = null;
573 663
@@ -579,12 +669,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
579 { 669 {
580 if (!sp.IsChildAgent) 670 if (!sp.IsChildAgent)
581 { 671 {
582 if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Found root agent for client : {0}", sp.ControllingClient.Name); 672 if (m_debugEnabled)
673 m_log.DebugFormat("[GROUPS-MESSAGING]: Found root agent for client : {0}", sp.ControllingClient.Name);
674
583 return sp.ControllingClient; 675 return sp.ControllingClient;
584 } 676 }
585 else 677 else
586 { 678 {
587 if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Found child agent for client : {0}", sp.ControllingClient.Name); 679 if (m_debugEnabled)
680 m_log.DebugFormat("[GROUPS-MESSAGING]: Found child agent for client : {0}", sp.ControllingClient.Name);
681
588 child = sp.ControllingClient; 682 child = sp.ControllingClient;
589 } 683 }
590 } 684 }
@@ -593,12 +687,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
593 // If we didn't find a root, then just return whichever child we found, or null if none 687 // If we didn't find a root, then just return whichever child we found, or null if none
594 if (child == null) 688 if (child == null)
595 { 689 {
596 if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Could not find local client for agent : {0}", agentID); 690 if (m_debugEnabled)
691 m_log.DebugFormat("[GROUPS-MESSAGING]: Could not find local client for agent : {0}", agentID);
597 } 692 }
598 else 693 else
599 { 694 {
600 if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Returning child agent for client : {0}", child.Name); 695 if (m_debugEnabled)
696 m_log.DebugFormat("[GROUPS-MESSAGING]: Returning child agent for client : {0}", child.Name);
601 } 697 }
698
602 return child; 699 return child;
603 } 700 }
604 701
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
index 29f9591..1565da9 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs
@@ -35,10 +35,10 @@ using Nini.Config;
35using OpenMetaverse; 35using OpenMetaverse;
36using OpenMetaverse.StructuredData; 36using OpenMetaverse.StructuredData;
37using OpenSim.Framework; 37using OpenSim.Framework;
38using OpenSim.Framework.Communications;
39using OpenSim.Region.Framework.Interfaces; 38using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
41using OpenSim.Services.Interfaces; 40using OpenSim.Services.Interfaces;
41using System.Text;
42using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; 42using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags;
43 43
44namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups 44namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
@@ -76,9 +76,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
76 76
77 private List<Scene> m_sceneList = new List<Scene>(); 77 private List<Scene> m_sceneList = new List<Scene>();
78 78
79 private IMessageTransferModule m_msgTransferModule = null; 79 private IMessageTransferModule m_msgTransferModule;
80
81 private IGroupsMessagingModule m_groupsMessagingModule;
80 82
81 private IGroupsServicesConnector m_groupData = null; 83 private IGroupsServicesConnector m_groupData;
82 84
83 // Configuration settings 85 // Configuration settings
84 private bool m_groupsEnabled = false; 86 private bool m_groupsEnabled = false;
@@ -184,10 +186,19 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
184 if (m_msgTransferModule == null) 186 if (m_msgTransferModule == null)
185 { 187 {
186 m_groupsEnabled = false; 188 m_groupsEnabled = false;
187 m_log.Warn("[GROUPS]: Could not get MessageTransferModule"); 189 m_log.Warn("[GROUPS]: Could not get IMessageTransferModule");
188 } 190 }
189 } 191 }
190 192
193 if (m_groupsMessagingModule == null)
194 {
195 m_groupsMessagingModule = scene.RequestModuleInterface<IGroupsMessagingModule>();
196
197 // No message transfer module, no notices, group invites, rejects, ejects, etc
198 if (m_groupsMessagingModule == null)
199 m_log.Warn("[GROUPS]: Could not get IGroupsMessagingModule");
200 }
201
191 lock (m_sceneList) 202 lock (m_sceneList)
192 { 203 {
193 m_sceneList.Add(scene); 204 m_sceneList.Add(scene);
@@ -250,7 +261,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
250 261
251 client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; 262 client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest;
252 client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; 263 client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest;
253 client.OnDirFindQuery += OnDirFindQuery;
254 client.OnRequestAvatarProperties += OnRequestAvatarProperties; 264 client.OnRequestAvatarProperties += OnRequestAvatarProperties;
255 265
256 // Used for Notices and Group Invites/Accept/Reject 266 // Used for Notices and Group Invites/Accept/Reject
@@ -303,21 +313,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
303 } 313 }
304 */ 314 */
305 315
306 void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart)
307 {
308 if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups)
309 {
310 if (m_debugEnabled)
311 m_log.DebugFormat(
312 "[GROUPS]: {0} called with queryText({1}) queryFlags({2}) queryStart({3})",
313 System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart);
314
315 // TODO: This currently ignores pretty much all the query flags including Mature and sort order
316 remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(GetRequestingAgentID(remoteClient), queryText).ToArray());
317 }
318
319 }
320
321 private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) 316 private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
322 { 317 {
323 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 318 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
@@ -361,7 +356,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
361 356
362 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) 357 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
363 { 358 {
364 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 359 if (m_debugEnabled)
360 m_log.DebugFormat(
361 "[GROUPS]: {0} called for {1}, message type {2}",
362 System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name, (InstantMessageDialog)im.dialog);
365 363
366 // Group invitations 364 // Group invitations
367 if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) 365 if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
@@ -437,81 +435,160 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
437 string Subject = im.message.Substring(0, im.message.IndexOf('|')); 435 string Subject = im.message.Substring(0, im.message.IndexOf('|'));
438 string Message = im.message.Substring(Subject.Length + 1); 436 string Message = im.message.Substring(Subject.Length + 1);
439 437
438 InventoryItemBase item = null;
439 bool hasAttachment = false;
440 UUID itemID = UUID.Zero; //Assignment to quiet compiler
441 UUID ownerID = UUID.Zero; //Assignment to quiet compiler
440 byte[] bucket; 442 byte[] bucket;
441 443
442 if ((im.binaryBucket.Length == 1) && (im.binaryBucket[0] == 0)) 444 if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0)
443 {
444 bucket = new byte[19];
445 bucket[0] = 0; //dunno
446 bucket[1] = 0; //dunno
447 GroupID.ToBytes(bucket, 2);
448 bucket[18] = 0; //dunno
449 }
450 else
451 { 445 {
452 string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); 446 string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
453 binBucket = binBucket.Remove(0, 14).Trim(); 447 binBucket = binBucket.Remove(0, 14).Trim();
454 if (m_debugEnabled) 448
449 OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket);
450 if (binBucketOSD is OSD)
455 { 451 {
456 m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket); 452 OSDMap binBucketMap = (OSDMap)binBucketOSD;
453
454 itemID = binBucketMap["item_id"].AsUUID();
455 ownerID = binBucketMap["owner_id"].AsUUID();
456
457 //Attempt to get the details of the attached item.
458 //If sender doesn't own the attachment, the item
459 //variable will be set to null and attachment will
460 //not be included with the group notice.
461 Scene scene = (Scene)remoteClient.Scene;
462 item = new InventoryItemBase(itemID, ownerID);
463 item = scene.InventoryService.GetItem(item);
457 464
458 OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket); 465 if (item != null)
459
460 foreach (string key in binBucketOSD.Keys)
461 { 466 {
462 if (binBucketOSD.ContainsKey(key)) 467 //Got item details so include the attachment.
463 { 468 hasAttachment = true;
464 m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString());
465 }
466 } 469 }
467 } 470 }
468 471 else
469 // treat as if no attachment 472 {
473 m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType());
474 }
475 }
476
477 if (hasAttachment)
478 {
479 //Bucket contains information about attachment.
480 //
481 //Byte offset and description of bucket data:
482 //0: 1 byte indicating if attachment is present
483 //1: 1 byte indicating the type of attachment
484 //2: 16 bytes - Group UUID
485 //18: 16 bytes - UUID of the attachment owner
486 //34: 16 bytes - UUID of the attachment
487 //50: variable - Name of the attachment
488 //??: NUL byte to terminate the attachment name
489 byte[] name = Encoding.UTF8.GetBytes(item.Name);
490 bucket = new byte[51 + name.Length];//3 bytes, 3 UUIDs, and name
491 bucket[0] = 1; //Has attachment flag
492 bucket[1] = (byte)item.InvType; //Type of Attachment
493 GroupID.ToBytes(bucket, 2);
494 ownerID.ToBytes(bucket, 18);
495 itemID.ToBytes(bucket, 34);
496 name.CopyTo(bucket, 50);
497 }
498 else
499 {
470 bucket = new byte[19]; 500 bucket = new byte[19];
471 bucket[0] = 0; //dunno 501 bucket[0] = 0; //Has attachment flag
472 bucket[1] = 0; //dunno 502 bucket[1] = 0; //Type of attachment
473 GroupID.ToBytes(bucket, 2); 503 GroupID.ToBytes(bucket, 2);
474 bucket[18] = 0; //dunno 504 bucket[18] = 0; //NUL terminate name of attachment
475 } 505 }
476 506
477
478 m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket); 507 m_groupData.AddGroupNotice(GetRequestingAgentID(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket);
479 if (OnNewGroupNotice != null) 508 if (OnNewGroupNotice != null)
480 { 509 {
481 OnNewGroupNotice(GroupID, NoticeID); 510 OnNewGroupNotice(GroupID, NoticeID);
482 } 511 }
483 512
484 // Send notice out to everyone that wants notices 513 if (m_debugEnabled)
485 foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID))
486 { 514 {
487 if (m_debugEnabled) 515 foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), GroupID))
488 { 516 {
489 UserAccount targetUser = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, member.AgentID); 517 if (m_debugEnabled)
490 if (targetUser != null)
491 { 518 {
492 m_log.DebugFormat("[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices); 519 UserAccount targetUser
493 } 520 = m_sceneList[0].UserAccountService.GetUserAccount(
494 else 521 remoteClient.Scene.RegionInfo.ScopeID, member.AgentID);
495 { 522
496 m_log.DebugFormat("[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})", NoticeID, member.AgentID, member.AcceptNotices); 523 if (targetUser != null)
524 {
525 m_log.DebugFormat(
526 "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
527 NoticeID, targetUser.FirstName + " " + targetUser.LastName, member.AcceptNotices);
528 }
529 else
530 {
531 m_log.DebugFormat(
532 "[GROUPS]: Prepping group notice {0} for agent: {1} who Accepts Notices ({2})",
533 NoticeID, member.AgentID, member.AcceptNotices);
534 }
497 } 535 }
498 } 536 }
537 }
499 538
500 if (member.AcceptNotices) 539 GridInstantMessage msg
501 { 540 = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
502 // Build notice IIM
503 GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
504 541
505 msg.toAgentID = member.AgentID.Guid; 542 if (m_groupsMessagingModule != null)
506 OutgoingInstantMessage(msg, member.AgentID); 543 m_groupsMessagingModule.SendMessageToGroup(
507 } 544 msg, GroupID, remoteClient.AgentId, gmd => gmd.AcceptNotices);
545 }
546 }
547
548 if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted)
549 {
550 //Is bucket large enough to hold UUID of the attachment?
551 if (im.binaryBucket.Length < 16)
552 return;
553
554 UUID noticeID = new UUID(im.imSessionID);
555
556 if (m_debugEnabled)
557 m_log.DebugFormat("[GROUPS]: Requesting notice {0} for {1}", noticeID, remoteClient.AgentId);
558
559 GroupNoticeInfo notice = m_groupData.GetGroupNotice(GetRequestingAgentID(remoteClient), noticeID);
560 if (notice != null)
561 {
562 UUID giver = new UUID(notice.BinaryBucket, 18);
563 UUID attachmentUUID = new UUID(notice.BinaryBucket, 34);
564
565 if (m_debugEnabled)
566 m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId);
567
568 string message;
569 InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId,
570 giver, attachmentUUID, out message);
571
572 if (itemCopy == null)
573 {
574 remoteClient.SendAgentAlertMessage(message, false);
575 return;
508 } 576 }
577
578 remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0);
579 }
580 else
581 {
582 if (m_debugEnabled)
583 m_log.DebugFormat(
584 "[GROUPS]: Could not find notice {0} for {1} on GroupNoticeInventoryAccepted.",
585 noticeID, remoteClient.AgentId);
509 } 586 }
510 } 587 }
511 588
512 // Interop, received special 210 code for ejecting a group member 589 // Interop, received special 210 code for ejecting a group member
513 // this only works within the comms servers domain, and won't work hypergrid 590 // this only works within the comms servers domain, and won't work hypergrid
514 // TODO:FIXME: Use a presense server of some kind to find out where the 591 // TODO:FIXME: Use a presence server of some kind to find out where the
515 // client actually is, and try contacting that region directly to notify them, 592 // client actually is, and try contacting that region directly to notify them,
516 // or provide the notification via xmlrpc update queue 593 // or provide the notification via xmlrpc update queue
517 if ((im.dialog == 210)) 594 if ((im.dialog == 210))
@@ -764,7 +841,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
764 remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got insufficient funds to create a group."); 841 remoteClient.SendCreateGroupReply(UUID.Zero, false, "You have got insufficient funds to create a group.");
765 return UUID.Zero; 842 return UUID.Zero;
766 } 843 }
767 money.ApplyCharge(GetRequestingAgentID(remoteClient), money.GroupCreationCharge, "Group Creation"); 844 money.ApplyCharge(GetRequestingAgentID(remoteClient), money.GroupCreationCharge, MoneyTransactionType.GroupCreate);
768 } 845 }
769 UUID groupID = m_groupData.CreateGroup(GetRequestingAgentID(remoteClient), name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, GetRequestingAgentID(remoteClient)); 846 UUID groupID = m_groupData.CreateGroup(GetRequestingAgentID(remoteClient), name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, GetRequestingAgentID(remoteClient));
770 847
@@ -889,26 +966,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
889 966
890 if (data != null) 967 if (data != null)
891 { 968 {
892 GroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentID(remoteClient), data.GroupID, null); 969 GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested);
893
894 GridInstantMessage msg = new GridInstantMessage();
895 msg.imSessionID = UUID.Zero.Guid;
896 msg.fromAgentID = data.GroupID.Guid;
897 msg.toAgentID = GetRequestingAgentID(remoteClient).Guid;
898 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
899 msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName;
900 msg.message = data.noticeData.Subject + "|" + data.Message;
901 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested;
902 msg.fromGroup = true;
903 msg.offline = (byte)0;
904 msg.ParentEstateID = 0;
905 msg.Position = Vector3.Zero;
906 msg.RegionID = UUID.Zero.Guid;
907 msg.binaryBucket = data.BinaryBucket;
908 970
909 OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); 971 OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient));
910 } 972 }
911
912 } 973 }
913 974
914 public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) 975 public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
@@ -916,10 +977,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
916 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 977 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
917 978
918 GridInstantMessage msg = new GridInstantMessage(); 979 GridInstantMessage msg = new GridInstantMessage();
919 msg.imSessionID = UUID.Zero.Guid; 980 byte[] bucket;
981
982 msg.imSessionID = groupNoticeID.Guid;
920 msg.toAgentID = agentID.Guid; 983 msg.toAgentID = agentID.Guid;
921 msg.dialog = dialog; 984 msg.dialog = dialog;
922 // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice;
923 msg.fromGroup = true; 985 msg.fromGroup = true;
924 msg.offline = (byte)0; 986 msg.offline = (byte)0;
925 msg.ParentEstateID = 0; 987 msg.ParentEstateID = 0;
@@ -933,13 +995,38 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
933 msg.timestamp = info.noticeData.Timestamp; 995 msg.timestamp = info.noticeData.Timestamp;
934 msg.fromAgentName = info.noticeData.FromName; 996 msg.fromAgentName = info.noticeData.FromName;
935 msg.message = info.noticeData.Subject + "|" + info.Message; 997 msg.message = info.noticeData.Subject + "|" + info.Message;
936 msg.binaryBucket = info.BinaryBucket; 998
999 if (info.BinaryBucket[0] > 0)
1000 {
1001 //32 is due to not needing space for two of the UUIDs.
1002 //(Don't need UUID of attachment or its owner in IM)
1003 //50 offset gets us to start of attachment name.
1004 //We are skipping the attachment flag, type, and
1005 //the three UUID fields at the start of the bucket.
1006 bucket = new byte[info.BinaryBucket.Length-32];
1007 bucket[0] = 1; //Has attachment
1008 bucket[1] = info.BinaryBucket[1];
1009 Array.Copy(info.BinaryBucket, 50,
1010 bucket, 18, info.BinaryBucket.Length-50);
1011 }
1012 else
1013 {
1014 bucket = new byte[19];
1015 bucket[0] = 0; //No attachment
1016 bucket[1] = 0; //Attachment type
1017 bucket[18] = 0; //NUL terminate name
1018 }
1019
1020 info.GroupID.ToBytes(bucket, 2);
1021 msg.binaryBucket = bucket;
937 } 1022 }
938 else 1023 else
939 { 1024 {
940 if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID); 1025 if (m_debugEnabled)
1026 m_log.DebugFormat("[GROUPS]: Group Notice {0} not found, composing empty message.", groupNoticeID);
1027
941 msg.fromAgentID = UUID.Zero.Guid; 1028 msg.fromAgentID = UUID.Zero.Guid;
942 msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; 1029 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
943 msg.fromAgentName = string.Empty; 1030 msg.fromAgentName = string.Empty;
944 msg.message = string.Empty; 1031 msg.message = string.Empty;
945 msg.binaryBucket = new byte[0]; 1032 msg.binaryBucket = new byte[0];
@@ -1063,7 +1150,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1063 // Message to ejector 1150 // Message to ejector
1064 // Interop, received special 210 code for ejecting a group member 1151 // Interop, received special 210 code for ejecting a group member
1065 // this only works within the comms servers domain, and won't work hypergrid 1152 // this only works within the comms servers domain, and won't work hypergrid
1066 // TODO:FIXME: Use a presense server of some kind to find out where the 1153 // TODO:FIXME: Use a presence server of some kind to find out where the
1067 // client actually is, and try contacting that region directly to notify them, 1154 // client actually is, and try contacting that region directly to notify them,
1068 // or provide the notification via xmlrpc update queue 1155 // or provide the notification via xmlrpc update queue
1069 1156
@@ -1178,6 +1265,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1178 } 1265 }
1179 } 1266 }
1180 1267
1268 public List<DirGroupsReplyData> FindGroups(IClientAPI remoteClient, string query)
1269 {
1270 return m_groupData.FindGroups(GetRequestingAgentID(remoteClient), query);
1271 }
1272
1273
1181 #endregion 1274 #endregion
1182 1275
1183 #region Client/Update Tools 1276 #region Client/Update Tools
@@ -1222,7 +1315,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1222 AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID)); 1315 AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID));
1223 AgentData.Add(AgentDataMap); 1316 AgentData.Add(AgentDataMap);
1224 1317
1225
1226 OSDArray GroupData = new OSDArray(data.Length); 1318 OSDArray GroupData = new OSDArray(data.Length);
1227 OSDArray NewGroupData = new OSDArray(data.Length); 1319 OSDArray NewGroupData = new OSDArray(data.Length);
1228 1320
@@ -1288,7 +1380,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1288 presence.Grouptitle = Title; 1380 presence.Grouptitle = Title;
1289 1381
1290 if (! presence.IsChildAgent) 1382 if (! presence.IsChildAgent)
1291 presence.SendAvatarDataToAllAgents(); 1383 presence.SendAvatarDataToAllClients();
1292 } 1384 }
1293 } 1385 }
1294 } 1386 }
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
index 7bae8f7..1cb4747 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
@@ -42,7 +42,6 @@ using OpenMetaverse;
42using OpenMetaverse.StructuredData; 42using OpenMetaverse.StructuredData;
43 43
44using OpenSim.Framework; 44using OpenSim.Framework;
45using OpenSim.Framework.Communications;
46using OpenSim.Region.Framework.Interfaces; 45using OpenSim.Region.Framework.Interfaces;
47using OpenSim.Services.Interfaces; 46using OpenSim.Services.Interfaces;
48 47
@@ -212,8 +211,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
212 m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name); 211 m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name);
213 212
214 m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); 213 m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty);
215 if ((m_groupsServerURI == null) || 214 if (string.IsNullOrEmpty(m_groupsServerURI))
216 (m_groupsServerURI == string.Empty))
217 { 215 {
218 m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]"); 216 m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]");
219 m_connectorEnabled = false; 217 m_connectorEnabled = false;
@@ -438,7 +436,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
438 return null; 436 return null;
439 } 437 }
440 } 438 }
441 else if ((groupName != null) && (groupName != string.Empty)) 439 else if (!string.IsNullOrEmpty(groupName))
442 { 440 {
443 if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap)) 441 if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap))
444 { 442 {
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
index c1bdacb..9a42bac 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
@@ -26,15 +26,25 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Net;
29using System.Reflection; 32using System.Reflection;
30using Nini.Config; 33using Nini.Config;
31using NUnit.Framework; 34using NUnit.Framework;
32using OpenMetaverse; 35using OpenMetaverse;
36using OpenMetaverse.Messages.Linden;
37using OpenMetaverse.Packets;
38using OpenMetaverse.StructuredData;
33using OpenSim.Framework; 39using OpenSim.Framework;
34using OpenSim.Framework.Communications; 40using OpenSim.Framework.Servers;
41using OpenSim.Framework.Servers.HttpServer;
42using OpenSim.Region.ClientStack.Linden;
43using OpenSim.Region.CoreModules.Avatar.InstantMessage;
44using OpenSim.Region.CoreModules.Framework;
35using OpenSim.Region.Framework.Scenes; 45using OpenSim.Region.Framework.Scenes;
46using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
36using OpenSim.Tests.Common; 47using OpenSim.Tests.Common;
37using OpenSim.Tests.Common.Mock;
38 48
39namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests 49namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests
40{ 50{
@@ -44,11 +54,28 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests
44 [TestFixture] 54 [TestFixture]
45 public class GroupsModuleTests : OpenSimTestCase 55 public class GroupsModuleTests : OpenSimTestCase
46 { 56 {
57 [SetUp]
58 public override void SetUp()
59 {
60 base.SetUp();
61
62 uint port = 9999;
63 uint sslPort = 9998;
64
65 // This is an unfortunate bit of clean up we have to do because MainServer manages things through static
66 // variables and the VM is not restarted between tests.
67 MainServer.RemoveHttpServer(port);
68
69 BaseHttpServer server = new BaseHttpServer(port, false, sslPort, "");
70 MainServer.AddHttpServer(server);
71 MainServer.Instance = server;
72 }
73
47 [Test] 74 [Test]
48 public void TestBasic() 75 public void TestSendAgentGroupDataUpdate()
49 { 76 {
50 TestHelpers.InMethod(); 77 TestHelpers.InMethod();
51// log4net.Config.XmlConfigurator.Configure(); 78// TestHelpers.EnableLogging();
52 79
53 TestScene scene = new SceneHelpers().SetupScene(); 80 TestScene scene = new SceneHelpers().SetupScene();
54 IConfigSource configSource = new IniConfigSource(); 81 IConfigSource configSource = new IniConfigSource();
@@ -56,8 +83,185 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests
56 config.Set("Enabled", true); 83 config.Set("Enabled", true);
57 config.Set("Module", "GroupsModule"); 84 config.Set("Module", "GroupsModule");
58 config.Set("DebugEnabled", true); 85 config.Set("DebugEnabled", true);
86
87 GroupsModule gm = new GroupsModule();
88 EventQueueGetModule eqgm = new EventQueueGetModule();
89
90 // We need a capabilities module active so that adding the scene presence creates an event queue in the
91 // EventQueueGetModule
59 SceneHelpers.SetupSceneModules( 92 SceneHelpers.SetupSceneModules(
60 scene, configSource, new object[] { new MockGroupsServicesConnector() }); 93 scene, configSource, gm, new MockGroupsServicesConnector(), new CapabilitiesModule(), eqgm);
94
95 ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseStem("1"));
96
97 gm.SendAgentGroupDataUpdate(sp.ControllingClient);
98
99 Hashtable eventsResponse = eqgm.GetEvents(UUID.Zero, sp.UUID);
100
101 Assert.That((int)eventsResponse["int_response_code"], Is.EqualTo((int)HttpStatusCode.OK));
102
103// Console.WriteLine("Response [{0}]", (string)eventsResponse["str_response_string"]);
104
105 OSDMap rawOsd = (OSDMap)OSDParser.DeserializeLLSDXml((string)eventsResponse["str_response_string"]);
106 OSDArray eventsOsd = (OSDArray)rawOsd["events"];
107
108 bool foundUpdate = false;
109 foreach (OSD osd in eventsOsd)
110 {
111 OSDMap eventOsd = (OSDMap)osd;
112
113 if (eventOsd["message"] == "AgentGroupDataUpdate")
114 foundUpdate = true;
115 }
116
117 Assert.That(foundUpdate, Is.True, "Did not find AgentGroupDataUpdate in response");
118
119 // TODO: More checking of more actual event data.
120 }
121
122 [Test]
123 public void TestSendGroupNotice()
124 {
125 TestHelpers.InMethod();
126// TestHelpers.EnableLogging();
127
128 TestScene scene = new SceneHelpers().SetupScene();
129
130 MessageTransferModule mtm = new MessageTransferModule();
131 GroupsModule gm = new GroupsModule();
132 GroupsMessagingModule gmm = new GroupsMessagingModule();
133 MockGroupsServicesConnector mgsc = new MockGroupsServicesConnector();
134
135 IConfigSource configSource = new IniConfigSource();
136
137 {
138 IConfig config = configSource.AddConfig("Messaging");
139 config.Set("MessageTransferModule", mtm.Name);
140 }
141
142 {
143 IConfig config = configSource.AddConfig("Groups");
144 config.Set("Enabled", true);
145 config.Set("Module", gm.Name);
146 config.Set("DebugEnabled", true);
147 config.Set("MessagingModule", gmm.Name);
148 config.Set("MessagingEnabled", true);
149 }
150
151 SceneHelpers.SetupSceneModules(scene, configSource, mgsc, mtm, gm, gmm);
152
153 UUID userId = TestHelpers.ParseTail(0x1);
154 string subjectText = "newman";
155 string messageText = "Hello";
156 string combinedSubjectMessage = string.Format("{0}|{1}", subjectText, messageText);
157
158 ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x1));
159 TestClient tc = (TestClient)sp.ControllingClient;
160
161 UUID groupID = gm.CreateGroup(tc, "group1", null, true, UUID.Zero, 0, true, true, true);
162 gm.JoinGroupRequest(tc, groupID);
163
164 // Create a second user who doesn't want to receive notices
165 ScenePresence sp2 = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x2));
166 TestClient tc2 = (TestClient)sp2.ControllingClient;
167 gm.JoinGroupRequest(tc2, groupID);
168 gm.SetGroupAcceptNotices(tc2, groupID, false, true);
169
170 List<GridInstantMessage> spReceivedMessages = new List<GridInstantMessage>();
171 tc.OnReceivedInstantMessage += im => spReceivedMessages.Add(im);
172
173 List<GridInstantMessage> sp2ReceivedMessages = new List<GridInstantMessage>();
174 tc2.OnReceivedInstantMessage += im => sp2ReceivedMessages.Add(im);
175
176 GridInstantMessage noticeIm = new GridInstantMessage();
177 noticeIm.fromAgentID = userId.Guid;
178 noticeIm.toAgentID = groupID.Guid;
179 noticeIm.message = combinedSubjectMessage;
180 noticeIm.dialog = (byte)InstantMessageDialog.GroupNotice;
181
182 tc.HandleImprovedInstantMessage(noticeIm);
183
184 Assert.That(spReceivedMessages.Count, Is.EqualTo(1));
185 Assert.That(spReceivedMessages[0].message, Is.EqualTo(combinedSubjectMessage));
186
187 List<GroupNoticeData> notices = mgsc.GetGroupNotices(UUID.Zero, groupID);
188 Assert.AreEqual(1, notices.Count);
189
190 // OpenSimulator (possibly also SL) transport the notice ID as the session ID!
191 Assert.AreEqual(notices[0].NoticeID.Guid, spReceivedMessages[0].imSessionID);
192
193 Assert.That(sp2ReceivedMessages.Count, Is.EqualTo(0));
194 }
195
196 /// <summary>
197 /// Run test with the MessageOnlineUsersOnly flag set.
198 /// </summary>
199 [Test]
200 public void TestSendGroupNoticeOnlineOnly()
201 {
202 TestHelpers.InMethod();
203 // TestHelpers.EnableLogging();
204
205 TestScene scene = new SceneHelpers().SetupScene();
206
207 MessageTransferModule mtm = new MessageTransferModule();
208 GroupsModule gm = new GroupsModule();
209 GroupsMessagingModule gmm = new GroupsMessagingModule();
210
211 IConfigSource configSource = new IniConfigSource();
212
213 {
214 IConfig config = configSource.AddConfig("Messaging");
215 config.Set("MessageTransferModule", mtm.Name);
216 }
217
218 {
219 IConfig config = configSource.AddConfig("Groups");
220 config.Set("Enabled", true);
221 config.Set("Module", gm.Name);
222 config.Set("DebugEnabled", true);
223 config.Set("MessagingModule", gmm.Name);
224 config.Set("MessagingEnabled", true);
225 config.Set("MessageOnlineUsersOnly", true);
226 }
227
228 SceneHelpers.SetupSceneModules(scene, configSource, new MockGroupsServicesConnector(), mtm, gm, gmm);
229
230 UUID userId = TestHelpers.ParseTail(0x1);
231 string subjectText = "newman";
232 string messageText = "Hello";
233 string combinedSubjectMessage = string.Format("{0}|{1}", subjectText, messageText);
234
235 ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x1));
236 TestClient tc = (TestClient)sp.ControllingClient;
237
238 UUID groupID = gm.CreateGroup(tc, "group1", null, true, UUID.Zero, 0, true, true, true);
239 gm.JoinGroupRequest(tc, groupID);
240
241 // Create a second user who doesn't want to receive notices
242 ScenePresence sp2 = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x2));
243 TestClient tc2 = (TestClient)sp2.ControllingClient;
244 gm.JoinGroupRequest(tc2, groupID);
245 gm.SetGroupAcceptNotices(tc2, groupID, false, true);
246
247 List<GridInstantMessage> spReceivedMessages = new List<GridInstantMessage>();
248 tc.OnReceivedInstantMessage += im => spReceivedMessages.Add(im);
249
250 List<GridInstantMessage> sp2ReceivedMessages = new List<GridInstantMessage>();
251 tc2.OnReceivedInstantMessage += im => sp2ReceivedMessages.Add(im);
252
253 GridInstantMessage noticeIm = new GridInstantMessage();
254 noticeIm.fromAgentID = userId.Guid;
255 noticeIm.toAgentID = groupID.Guid;
256 noticeIm.message = combinedSubjectMessage;
257 noticeIm.dialog = (byte)InstantMessageDialog.GroupNotice;
258
259 tc.HandleImprovedInstantMessage(noticeIm);
260
261 Assert.That(spReceivedMessages.Count, Is.EqualTo(1));
262 Assert.That(spReceivedMessages[0].message, Is.EqualTo(combinedSubjectMessage));
263
264 Assert.That(sp2ReceivedMessages.Count, Is.EqualTo(0));
61 } 265 }
62 } 266 }
63} \ No newline at end of file 267} \ No newline at end of file
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
index 1101851..20555e4 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
@@ -41,7 +41,6 @@ using OpenMetaverse;
41using OpenMetaverse.StructuredData; 41using OpenMetaverse.StructuredData;
42 42
43using OpenSim.Framework; 43using OpenSim.Framework;
44using OpenSim.Framework.Communications;
45using OpenSim.Region.Framework.Interfaces; 44using OpenSim.Region.Framework.Interfaces;
46using OpenSim.Services.Interfaces; 45using OpenSim.Services.Interfaces;
47 46
@@ -168,8 +167,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
168 m_log.DebugFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name); 167 m_log.DebugFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name);
169 168
170 m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); 169 m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty);
171 if ((m_groupsServerURI == null) || 170 if (string.IsNullOrEmpty(m_groupsServerURI))
172 (m_groupsServerURI == string.Empty))
173 { 171 {
174 m_log.ErrorFormat("Please specify a valid URL for GroupsServerURI in OpenSim.ini, [Groups]"); 172 m_log.ErrorFormat("Please specify a valid URL for GroupsServerURI in OpenSim.ini, [Groups]");
175 m_connectorEnabled = false; 173 m_connectorEnabled = false;
@@ -354,7 +352,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
354 { 352 {
355 param["GroupID"] = GroupID.ToString(); 353 param["GroupID"] = GroupID.ToString();
356 } 354 }
357 if ((GroupName != null) && (GroupName != string.Empty)) 355 if (!string.IsNullOrEmpty(GroupName))
358 { 356 {
359 param["Name"] = GroupName.ToString(); 357 param["Name"] = GroupName.ToString();
360 } 358 }
@@ -1013,7 +1011,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1013 Hashtable respData = (Hashtable)resp.Value; 1011 Hashtable respData = (Hashtable)resp.Value;
1014 if (respData.Contains("error") && !respData.Contains("succeed")) 1012 if (respData.Contains("error") && !respData.Contains("succeed"))
1015 { 1013 {
1016 LogRespDataToConsoleError(respData); 1014 LogRespDataToConsoleError(requestingAgentID, function, param, respData);
1017 } 1015 }
1018 1016
1019 return respData; 1017 return respData;
@@ -1041,20 +1039,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1041 return error; 1039 return error;
1042 } 1040 }
1043 1041
1044 private void LogRespDataToConsoleError(Hashtable respData) 1042 private void LogRespDataToConsoleError(UUID requestingAgentID, string function, Hashtable param, Hashtable respData)
1045 { 1043 {
1046 m_log.Error("[XMLRPC-GROUPS-CONNECTOR]: Error:"); 1044 m_log.ErrorFormat(
1047 1045 "[XMLRPC-GROUPS-CONNECTOR]: Error when calling {0} for {1} with params {2}. Response params are {3}",
1048 foreach (string key in respData.Keys) 1046 function, requestingAgentID, Util.PrettyFormatToSingleLine(param), Util.PrettyFormatToSingleLine(respData));
1049 {
1050 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Key: {0}", key);
1051
1052 string[] lines = respData[key].ToString().Split(new char[] { '\n' });
1053 foreach (string line in lines)
1054 {
1055 m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0}", line);
1056 }
1057 }
1058 } 1047 }
1059 1048
1060 /// <summary> 1049 /// <summary>
@@ -1146,28 +1135,38 @@ namespace Nwc.XmlRpc
1146 request.AllowWriteStreamBuffering = true; 1135 request.AllowWriteStreamBuffering = true;
1147 request.KeepAlive = !_disableKeepAlive; 1136 request.KeepAlive = !_disableKeepAlive;
1148 1137
1149 Stream stream = request.GetRequestStream(); 1138 using (Stream stream = request.GetRequestStream())
1150 XmlTextWriter xml = new XmlTextWriter(stream, Encoding.ASCII);
1151 _serializer.Serialize(xml, this);
1152 xml.Flush();
1153 xml.Close();
1154
1155 HttpWebResponse response = (HttpWebResponse)request.GetResponse();
1156 StreamReader input = new StreamReader(response.GetResponseStream());
1157
1158 string inputXml = input.ReadToEnd();
1159 XmlRpcResponse resp;
1160 try
1161 { 1139 {
1162 resp = (XmlRpcResponse)_deserializer.Deserialize(inputXml); 1140 using (XmlTextWriter xml = new XmlTextWriter(stream, Encoding.ASCII))
1141 {
1142 _serializer.Serialize(xml, this);
1143 xml.Flush();
1144 }
1163 } 1145 }
1164 catch (Exception e) 1146
1147 XmlRpcResponse resp;
1148
1149 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
1165 { 1150 {
1166 RequestResponse = inputXml; 1151 using (Stream s = response.GetResponseStream())
1167 throw e; 1152 {
1153 using (StreamReader input = new StreamReader(s))
1154 {
1155 string inputXml = input.ReadToEnd();
1156
1157 try
1158 {
1159 resp = (XmlRpcResponse)_deserializer.Deserialize(inputXml);
1160 }
1161 catch (Exception e)
1162 {
1163 RequestResponse = inputXml;
1164 throw e;
1165 }
1166 }
1167 }
1168 } 1168 }
1169 input.Close(); 1169
1170 response.Close();
1171 return resp; 1170 return resp;
1172 } 1171 }
1173 } 1172 }