aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups
diff options
context:
space:
mode:
authorCharles Krinke2009-04-25 18:58:18 +0000
committerCharles Krinke2009-04-25 18:58:18 +0000
commitc17a12544561de8103e1631c1a23dd225bd39698 (patch)
tree104af1bbb1dd1724b38731b37cf9b82f8d0b6fab /OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups
parentFix another typo in the ini example (diff)
downloadopensim-SC_OLD-c17a12544561de8103e1631c1a23dd225bd39698.zip
opensim-SC_OLD-c17a12544561de8103e1631c1a23dd225bd39698.tar.gz
opensim-SC_OLD-c17a12544561de8103e1631c1a23dd225bd39698.tar.bz2
opensim-SC_OLD-c17a12544561de8103e1631c1a23dd225bd39698.tar.xz
Thank you kindly, MCortez for a patch that:
The attached patch fixes a few problems that people were having with the Messaging provided by the XmlRpcGroups optional module, namely: * Fixes 2x echo in group messaging * Fixes problems with cross instance, non-neighbor, messaging
Diffstat (limited to 'OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups')
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs36
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs423
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs841
3 files changed, 795 insertions, 505 deletions
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs
index 2a5a319..27cffd6 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupData.cs
@@ -146,11 +146,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
146 | GroupPowers.VoteOnProposal; 146 | GroupPowers.VoteOnProposal;
147 param["OwnersPowers"] = ((ulong)OwnerPowers).ToString(); 147 param["OwnersPowers"] = ((ulong)OwnerPowers).ToString();
148 148
149
150
151
149 Hashtable respData = XmlRpcCall("groups.createGroup", param); 152 Hashtable respData = XmlRpcCall("groups.createGroup", param);
150 153
151 if (respData.Contains("error")) 154 if (respData.Contains("error"))
152 { 155 {
153 // UUID is not nullable 156 // UUID is not nullable
157
154 return UUID.Zero; 158 return UUID.Zero;
155 } 159 }
156 160
@@ -224,7 +228,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
224 public GroupRecord GetGroupRecord(UUID GroupID, string GroupName) 228 public GroupRecord GetGroupRecord(UUID GroupID, string GroupName)
225 { 229 {
226 Hashtable param = new Hashtable(); 230 Hashtable param = new Hashtable();
227 if (GroupID != UUID.Zero) 231 if ((GroupID != null) && (GroupID != UUID.Zero))
228 { 232 {
229 param["GroupID"] = GroupID.ToString(); 233 param["GroupID"] = GroupID.ToString();
230 } 234 }
@@ -233,6 +237,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
233 param["Name"] = GroupName.ToString(); 237 param["Name"] = GroupName.ToString();
234 } 238 }
235 239
240
236 Hashtable respData = XmlRpcCall("groups.getGroup", param); 241 Hashtable respData = XmlRpcCall("groups.getGroup", param);
237 242
238 if (respData.Contains("error")) 243 if (respData.Contains("error"))
@@ -249,6 +254,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
249 Hashtable param = new Hashtable(); 254 Hashtable param = new Hashtable();
250 param["GroupID"] = GroupID.ToString(); 255 param["GroupID"] = GroupID.ToString();
251 256
257
252 Hashtable respData = XmlRpcCall("groups.getGroup", param); 258 Hashtable respData = XmlRpcCall("groups.getGroup", param);
253 259
254 if (respData.Contains("error")) 260 if (respData.Contains("error"))
@@ -264,6 +270,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
264 MemberGroupProfile.PowersMask = MemberInfo.GroupPowers; 270 MemberGroupProfile.PowersMask = MemberInfo.GroupPowers;
265 271
266 return MemberGroupProfile; 272 return MemberGroupProfile;
273
267 } 274 }
268 275
269 private GroupProfileData GroupProfileHashtableToGroupProfileData(Hashtable groupProfile) 276 private GroupProfileData GroupProfileHashtableToGroupProfileData(Hashtable groupProfile)
@@ -294,6 +301,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
294 301
295 private GroupRecord GroupProfileHashtableToGroupRecord(Hashtable groupProfile) 302 private GroupRecord GroupProfileHashtableToGroupRecord(Hashtable groupProfile)
296 { 303 {
304
297 GroupRecord group = new GroupRecord(); 305 GroupRecord group = new GroupRecord();
298 group.GroupID = UUID.Parse((string)groupProfile["GroupID"]); 306 group.GroupID = UUID.Parse((string)groupProfile["GroupID"]);
299 group.GroupName = groupProfile["Name"].ToString(); 307 group.GroupName = groupProfile["Name"].ToString();
@@ -313,6 +321,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
313 return group; 321 return group;
314 } 322 }
315 323
324
316 public void SetAgentActiveGroup(UUID AgentID, UUID GroupID) 325 public void SetAgentActiveGroup(UUID AgentID, UUID GroupID)
317 { 326 {
318 Hashtable param = new Hashtable(); 327 Hashtable param = new Hashtable();
@@ -341,6 +350,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
341 param["ListInProfile"] = ListInProfile ? "1" : "0"; 350 param["ListInProfile"] = ListInProfile ? "1" : "0";
342 351
343 XmlRpcCall("groups.setAgentGroupInfo", param); 352 XmlRpcCall("groups.setAgentGroupInfo", param);
353
344 } 354 }
345 355
346 public void AddAgentToGroupInvite(UUID inviteID, UUID groupID, UUID roleID, UUID agentID) 356 public void AddAgentToGroupInvite(UUID inviteID, UUID groupID, UUID roleID, UUID agentID)
@@ -352,6 +362,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
352 param["GroupID"] = groupID.ToString(); 362 param["GroupID"] = groupID.ToString();
353 363
354 XmlRpcCall("groups.addAgentToGroupInvite", param); 364 XmlRpcCall("groups.addAgentToGroupInvite", param);
365
355 } 366 }
356 367
357 public GroupInviteInfo GetAgentToGroupInvite(UUID inviteID) 368 public GroupInviteInfo GetAgentToGroupInvite(UUID inviteID)
@@ -400,6 +411,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
400 param["GroupID"] = GroupID.ToString(); 411 param["GroupID"] = GroupID.ToString();
401 412
402 XmlRpcCall("groups.removeAgentFromGroup", param); 413 XmlRpcCall("groups.removeAgentFromGroup", param);
414
403 } 415 }
404 416
405 public void AddAgentToGroupRole(UUID AgentID, UUID GroupID, UUID RoleID) 417 public void AddAgentToGroupRole(UUID AgentID, UUID GroupID, UUID RoleID)
@@ -422,6 +434,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
422 XmlRpcCall("groups.removeAgentFromGroupRole", param); 434 XmlRpcCall("groups.removeAgentFromGroupRole", param);
423 } 435 }
424 436
437
425 public List<DirGroupsReplyData> FindGroups(string search) 438 public List<DirGroupsReplyData> FindGroups(string search)
426 { 439 {
427 Hashtable param = new Hashtable(); 440 Hashtable param = new Hashtable();
@@ -482,6 +495,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
482 return HashTableToGroupMembershipData(respData); 495 return HashTableToGroupMembershipData(respData);
483 } 496 }
484 497
498
485 public List<GroupMembershipData> GetAgentGroupMemberships(UUID AgentID) 499 public List<GroupMembershipData> GetAgentGroupMemberships(UUID AgentID)
486 { 500 {
487 Hashtable param = new Hashtable(); 501 Hashtable param = new Hashtable();
@@ -529,6 +543,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
529 } 543 }
530 544
531 return Roles; 545 return Roles;
546
547
532 } 548 }
533 549
534 public List<GroupRolesData> GetGroupRoles(UUID GroupID) 550 public List<GroupRolesData> GetGroupRoles(UUID GroupID)
@@ -559,6 +575,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
559 } 575 }
560 576
561 return Roles; 577 return Roles;
578
562 } 579 }
563 580
564 private static GroupMembershipData HashTableToGroupMembershipData(Hashtable respData) 581 private static GroupMembershipData HashTableToGroupMembershipData(Hashtable respData)
@@ -583,7 +600,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
583 data.AllowPublish = ((string)respData["AllowPublish"] == "1"); 600 data.AllowPublish = ((string)respData["AllowPublish"] == "1");
584 if (respData["Charter"] != null) 601 if (respData["Charter"] != null)
585 { 602 {
586 data.Charter = (string)respData["Charter"]; 603 data.Charter = (string)respData["Charter"];
587 } 604 }
588 data.FounderID = new UUID((string)respData["FounderID"]); 605 data.FounderID = new UUID((string)respData["FounderID"]);
589 data.GroupID = new UUID((string)respData["GroupID"]); 606 data.GroupID = new UUID((string)respData["GroupID"]);
@@ -626,6 +643,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
626 } 643 }
627 644
628 return members; 645 return members;
646
629 } 647 }
630 648
631 public List<GroupRoleMembersData> GetGroupRoleMembers(UUID GroupID) 649 public List<GroupRoleMembersData> GetGroupRoleMembers(UUID GroupID)
@@ -642,10 +660,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
642 foreach (Hashtable membership in respData.Values) 660 foreach (Hashtable membership in respData.Values)
643 { 661 {
644 GroupRoleMembersData data = new GroupRoleMembersData(); 662 GroupRoleMembersData data = new GroupRoleMembersData();
645 663
646 data.MemberID = new UUID((string)membership["AgentID"]); 664 data.MemberID = new UUID((string)membership["AgentID"]);
647 data.RoleID = new UUID((string)membership["RoleID"]); 665 data.RoleID = new UUID((string)membership["RoleID"]);
648 666
649 members.Add(data); 667 members.Add(data);
650 } 668 }
651 } 669 }
@@ -685,9 +703,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
685 param["NoticeID"] = noticeID.ToString(); 703 param["NoticeID"] = noticeID.ToString();
686 704
687 Hashtable respData = XmlRpcCall("groups.getGroupNotice", param); 705 Hashtable respData = XmlRpcCall("groups.getGroupNotice", param);
688
689 706
690 if (!respData.Contains("error")) 707
708 if (respData.Contains("error"))
691 { 709 {
692 return null; 710 return null;
693 } 711 }
@@ -775,7 +793,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
775 Hashtable error = new Hashtable(); 793 Hashtable error = new Hashtable();
776 error.Add("error", "invalid return value"); 794 error.Add("error", "invalid return value");
777 return error; 795 return error;
778 } 796 }
779 797
780 private void LogRespDataToConsoleError(Hashtable respData) 798 private void LogRespDataToConsoleError(Hashtable respData)
781 { 799 {
@@ -785,15 +803,15 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
785 { 803 {
786 m_log.ErrorFormat("[GROUPDATA] Key: {0}", key); 804 m_log.ErrorFormat("[GROUPDATA] Key: {0}", key);
787 805
788 // object o = respData[key];
789
790 string[] lines = respData[key].ToString().Split(new char[] { '\n' }); 806 string[] lines = respData[key].ToString().Split(new char[] { '\n' });
791 foreach (string line in lines) 807 foreach (string line in lines)
792 { 808 {
793 m_log.ErrorFormat("[GROUPDATA] {0}", line); 809 m_log.ErrorFormat("[GROUPDATA] {0}", line);
794 } 810 }
811
795 } 812 }
796 } 813 }
814
797 } 815 }
798 816
799 public class GroupNoticeInfo 817 public class GroupNoticeInfo
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs
index b1b25aa..b1322ab 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsMessaging.cs
@@ -26,7 +26,6 @@
26 */ 26 */
27 27
28using System; 28using System;
29//using System.Collections;
30using System.Collections.Generic; 29using System.Collections.Generic;
31using System.Reflection; 30using System.Reflection;
32 31
@@ -47,25 +46,27 @@ using Caps = OpenSim.Framework.Communications.Capabilities.Caps;
47 46
48namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups 47namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
49{ 48{
50 public class XmlRpcGroupsMessaging : INonSharedRegionModule 49 public class XmlRpcGroupsMessaging : ISharedRegionModule
51 { 50 {
52 51
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54 53
55 private List<Scene> m_SceneList = new List<Scene>(); 54 private List<Scene> m_sceneList = new List<Scene>();
56 55
57 // must be NonShared for this to work, otherewise we may actually get multiple active clients 56 private IMessageTransferModule m_msgTransferModule = null;
58 private Dictionary<Guid, IClientAPI> m_ActiveClients = new Dictionary<Guid, IClientAPI>();
59 57
60 private IMessageTransferModule m_MsgTransferModule = null; 58 private IGroupsModule m_groupsModule = null;
59
60 // TODO: Move this off to the xmlrpc server
61 public Dictionary<Guid, List<Guid>> m_agentsInGroupSession = new Dictionary<Guid, List<Guid>>();
62 public Dictionary<Guid, List<Guid>> m_agentsDroppedSession = new Dictionary<Guid, List<Guid>>();
61 63
62 private IGroupsModule m_GroupsModule = null;
63 64
64 // Config Options 65 // Config Options
65 private bool m_GroupMessagingEnabled = true; 66 private bool m_groupMessagingEnabled = true;
66 private bool m_debugEnabled = true; 67 private bool m_debugEnabled = true;
67 68
68 #region IRegionModule Members 69 #region IRegionModuleBase Members
69 70
70 public void Initialise(IConfigSource config) 71 public void Initialise(IConfigSource config)
71 { 72 {
@@ -90,14 +91,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
90 if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups") 91 if (groupsConfig.GetString("Module", "Default") != "XmlRpcGroups")
91 { 92 {
92 m_log.Info("[GROUPS-MESSAGING]: Config Groups Module not set to XmlRpcGroups"); 93 m_log.Info("[GROUPS-MESSAGING]: Config Groups Module not set to XmlRpcGroups");
93 m_GroupMessagingEnabled = false; 94 m_groupMessagingEnabled = false;
94 95
95 return; 96 return;
96 } 97 }
97 98
98 m_GroupMessagingEnabled = groupsConfig.GetBoolean("XmlRpcMessagingEnabled", true); 99 m_groupMessagingEnabled = groupsConfig.GetBoolean("XmlRpcMessagingEnabled", true);
99 100
100 if (!m_GroupMessagingEnabled) 101 if (!m_groupMessagingEnabled)
101 { 102 {
102 m_log.Info("[GROUPS-MESSAGING]: XmlRpcGroups Messaging disabled."); 103 m_log.Info("[GROUPS-MESSAGING]: XmlRpcGroups Messaging disabled.");
103 return; 104 return;
@@ -113,76 +114,74 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
113 114
114 public void AddRegion(Scene scene) 115 public void AddRegion(Scene scene)
115 { 116 {
117 // NoOp
116 } 118 }
117 public void RegionLoaded(Scene scene) 119 public void RegionLoaded(Scene scene)
118 { 120 {
119 if (!m_GroupMessagingEnabled) 121 if (!m_groupMessagingEnabled)
120 return; 122 return;
121 123
122 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 124 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
123 125
124 126
125 m_GroupsModule = scene.RequestModuleInterface<IGroupsModule>(); 127 m_groupsModule = scene.RequestModuleInterface<IGroupsModule>();
126 128
127 // No groups module, no groups messaging 129 // No groups module, no groups messaging
128 if (m_GroupsModule == null) 130 if (m_groupsModule == null)
129 { 131 {
130 m_GroupMessagingEnabled = false; 132 m_groupMessagingEnabled = false;
131 m_log.Info("[GROUPS-MESSAGING]: Could not get IGroupsModule, XmlRpcGroupsMessaging is now disabled."); 133 m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsModule, XmlRpcGroupsMessaging is now disabled.");
132 Close(); 134 Close();
133 return; 135 return;
134 } 136 }
135 137
136 m_MsgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>(); 138 m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
137 139
138 // No message transfer module, no groups messaging 140 // No message transfer module, no groups messaging
139 if (m_MsgTransferModule == null) 141 if (m_msgTransferModule == null)
140 { 142 {
141 m_GroupMessagingEnabled = false; 143 m_groupMessagingEnabled = false;
142 m_log.Info("[GROUPS-MESSAGING]: Could not get MessageTransferModule"); 144 m_log.Error("[GROUPS-MESSAGING]: Could not get MessageTransferModule");
143 Close(); 145 Close();
144 return; 146 return;
145 } 147 }
146 148
147 149
148 m_SceneList.Add(scene); 150 m_sceneList.Add(scene);
149 151
150 scene.EventManager.OnNewClient += OnNewClient; 152 scene.EventManager.OnNewClient += OnNewClient;
151 scene.EventManager.OnClientClosed += OnClientClosed;
152 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; 153 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
153 154
154 } 155 }
155 156
156 public void RemoveRegion(Scene scene) 157 public void RemoveRegion(Scene scene)
157 { 158 {
158 if (!m_GroupMessagingEnabled) 159 if (!m_groupMessagingEnabled)
159 return; 160 return;
160 161
161 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 162 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
162 163
163 m_SceneList.Remove(scene); 164 m_sceneList.Remove(scene);
164 } 165 }
165 166
166 167
167 public void Close() 168 public void Close()
168 { 169 {
169 if (!m_GroupMessagingEnabled) 170 if (!m_groupMessagingEnabled)
170 return; 171 return;
171 172
172 m_log.Debug("[GROUPS-MESSAGING]: Shutting down XmlRpcGroupsMessaging module."); 173 if(m_debugEnabled) m_log.Debug("[GROUPS-MESSAGING]: Shutting down XmlRpcGroupsMessaging module.");
173
174 174
175 foreach (Scene scene in m_SceneList) 175 foreach (Scene scene in m_sceneList)
176 { 176 {
177 scene.EventManager.OnNewClient -= OnNewClient; 177 scene.EventManager.OnNewClient -= OnNewClient;
178 scene.EventManager.OnClientClosed -= OnClientClosed;
179 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; 178 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
180 } 179 }
181 180
182 m_SceneList.Clear(); 181 m_sceneList.Clear();
183 182
184 m_GroupsModule = null; 183 m_groupsModule = null;
185 m_MsgTransferModule = null; 184 m_msgTransferModule = null;
186 } 185 }
187 186
188 public string Name 187 public string Name
@@ -192,195 +191,293 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
192 191
193 #endregion 192 #endregion
194 193
195 #region SimGridEventHandlers 194 #region ISharedRegionModule Members
196 195
197 private void OnNewClient(IClientAPI client) 196 public void PostInitialise()
198 { 197 {
199 RegisterClientAgent(client); 198 // NoOp
200 } 199 }
201 private void OnClientClosed(UUID AgentId) 200
201 #endregion
202
203 #region SimGridEventHandlers
204
205 private void OnNewClient(IClientAPI client)
202 { 206 {
203 UnregisterClientAgent(AgentId); 207 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] OnInstantMessage registered for {0}", client.Name);
208
209 client.OnInstantMessage += OnInstantMessage;
204 } 210 }
205 211
206 private void OnGridInstantMessage(GridInstantMessage msg) 212 private void OnGridInstantMessage(GridInstantMessage msg)
207 { 213 {
208 m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 214 // The instant message module will only deliver messages of dialog types:
215 // MessageFromAgent, StartTyping, StopTyping, MessageFromObject
216 //
217 // Any other message type will not be delivered to a client by the
218 // Instant Message Module
219
220
221 if (m_debugEnabled)
222 {
223 m_log.DebugFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
209 224
210 DebugGridInstantMessage(msg); 225 DebugGridInstantMessage(msg);
226 }
211 227
212 // Incoming message from a group 228 // Incoming message from a group
213 if ((msg.dialog == (byte)InstantMessageDialog.SessionSend) && (msg.fromGroup == true)) 229 if ((msg.fromGroup == true) &&
230 ( (msg.dialog == (byte)InstantMessageDialog.SessionSend)
231 || (msg.dialog == (byte)InstantMessageDialog.SessionAdd)
232 || (msg.dialog == (byte)InstantMessageDialog.SessionDrop)
233 ))
234 {
235 ProcessMessageFromGroupSession(msg);
236 }
237
238 }
239
240 private void ProcessMessageFromGroupSession(GridInstantMessage msg)
241 {
242 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID);
243
244 switch (msg.dialog)
245 {
246 case (byte)InstantMessageDialog.SessionAdd:
247 AddAgentToGroupSession(msg.fromAgentID, msg.imSessionID);
248 break;
249
250 case (byte)InstantMessageDialog.SessionDrop:
251 RemoveAgentFromGroupSession(msg.fromAgentID, msg.imSessionID);
252 break;
253
254 case (byte)InstantMessageDialog.SessionSend:
255 if (!m_agentsInGroupSession.ContainsKey(msg.toAgentID)
256 && !m_agentsDroppedSession.ContainsKey(msg.toAgentID))
214 { 257 {
215 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] OnGridInstantMessage from group session {0} going to agent {1}", msg.fromAgentID, msg.toAgentID); 258 // Agent not in session and hasn't dropped from session
259 // Add them to the session for now, and Invite them
260 AddAgentToGroupSession(msg.toAgentID, msg.imSessionID);
216 261
217 if (m_ActiveClients.ContainsKey(msg.toAgentID)) 262 UUID toAgentID = new UUID(msg.toAgentID);
263 IClientAPI activeClient = GetActiveClient(toAgentID);
264 if (activeClient != null)
218 { 265 {
219 UUID GroupID = new UUID(msg.fromAgentID); 266 UUID groupID = new UUID(msg.fromAgentID);
220 // SendMessageToGroup(im);
221 267
222 GroupRecord GroupInfo = m_GroupsModule.GetGroupRecord(GroupID); 268 GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID);
223 if (GroupInfo != null) 269 if (groupInfo != null)
224 { 270 {
225 // TODO: Check to see if already a member of session, if so, do not send chatterbox, just forward message 271 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Sending chatterbox invite instant message");
226
227 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Sending chatterbox invite instant message");
228 272
229 // Force? open the group session dialog??? 273 // Force? open the group session dialog???
230 IEventQueue eq = m_ActiveClients[msg.toAgentID].Scene.RequestModuleInterface<IEventQueue>(); 274 IEventQueue eq = activeClient.Scene.RequestModuleInterface<IEventQueue>();
231 eq.ChatterboxInvitation( 275 eq.ChatterboxInvitation(
232 GroupID, 276 groupID
233 GroupInfo.GroupName, 277 , groupInfo.GroupName
234 new UUID(msg.fromAgentID), 278 , new UUID(msg.fromAgentID)
235 msg.message, new UUID(msg.toAgentID), 279 , msg.message, new UUID(msg.toAgentID)
236 msg.fromAgentName, 280 , msg.fromAgentName
237 msg.dialog, 281 , msg.dialog
238 msg.timestamp, 282 , msg.timestamp
239 msg.offline == 1, 283 , msg.offline == 1
240 (int)msg.ParentEstateID, 284 , (int)msg.ParentEstateID
241 msg.Position, 285 , msg.Position
242 1, 286 , 1
243 new UUID(msg.imSessionID), 287 , new UUID(msg.imSessionID)
244 msg.fromGroup, 288 , msg.fromGroup
245 Utils.StringToBytes(GroupInfo.GroupName)); 289 , Utils.StringToBytes(groupInfo.GroupName)
290 );
246 291
247 eq.ChatterBoxSessionAgentListUpdates( 292 eq.ChatterBoxSessionAgentListUpdates(
248 new UUID(GroupID), 293 new UUID(groupID)
249 new UUID(msg.fromAgentID), 294 , new UUID(msg.fromAgentID)
250 new UUID(msg.toAgentID), 295 , new UUID(msg.toAgentID)
251 false, //canVoiceChat 296 , false //canVoiceChat
252 false, //isModerator 297 , false //isModerator
253 false); //text mute 298 , false //text mute
299 );
300
254 } 301 }
255 } 302 }
256 } 303 }
304 else if (!m_agentsDroppedSession.ContainsKey(msg.toAgentID))
305 {
306 // User hasn't dropped, so they're in the session,
307 // maybe we should deliver it.
308 IClientAPI client = GetActiveClient(new UUID(msg.toAgentID));
309 if (client != null)
310 {
311 // Deliver locally, directly
312 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Delivering to {0} locally", client.Name);
313 client.SendInstantMessage(msg);
314 }
315 else
316 {
317 m_log.WarnFormat("[GROUPS-MESSAGING] Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
318 }
319 }
320 break;
257 321
322 default:
323 m_log.WarnFormat("[GROUPS-MESSAGING] I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString());
324 break;
325 }
258 } 326 }
259 327
328
329
260 #endregion 330 #endregion
261 331
262 #region ClientEvents 332 #region ClientEvents
263 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
264 {
265 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
266
267 DebugGridInstantMessage(im);
268 333
269 // Start group IM session 334 private void RemoveAgentFromGroupSession(Guid agentID, Guid sessionID)
270 if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) 335 {
336 if( m_agentsInGroupSession.ContainsKey(sessionID) )
271 { 337 {
272 UUID GroupID = new UUID(im.toAgentID); 338 // If in session remove
273 339 if( m_agentsInGroupSession[sessionID].Contains(agentID) )
274 GroupRecord GroupInfo = m_GroupsModule.GetGroupRecord(GroupID);
275 if (GroupInfo != null)
276 { 340 {
277 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Start Group Session for {0}", GroupInfo.GroupName); 341 m_agentsInGroupSession[sessionID].Remove(agentID);
278 342 }
279 // remoteClient.SendInstantMessage(new GridInstantMessage(remoteClient.Scene, GroupID, GroupProfile.Name, remoteClient.AgentId, (byte)OpenMetaverse.InstantMessageDialog.SessionSend, true, "Welcome", GroupID, false, new Vector3(), new byte[0]));
280
281 ChatterBoxSessionStartReplyViaCaps(remoteClient, GroupInfo.GroupName, GroupID);
282 343
283 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>(); 344 // If not in dropped list, add
284 queue.ChatterBoxSessionAgentListUpdates( 345 if( !m_agentsDroppedSession[sessionID].Contains(agentID) )
285 new UUID(GroupID), 346 {
286 new UUID(im.fromAgentID), 347 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Dropped {1} from session {0}", sessionID, agentID);
287 new UUID(im.toAgentID), 348 m_agentsDroppedSession[sessionID].Add(agentID);
288 false, //canVoiceChat
289 false, //isModerator
290 false); //text mute
291 } 349 }
292 } 350 }
351 }
293 352
294 // Send a message to a group 353 private void AddAgentToGroupSession(Guid agentID, Guid sessionID)
295 if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) 354 {
296 { 355 // Add Session Status if it doesn't exist for this session
297 UUID GroupID = new UUID(im.toAgentID); 356 CreateGroupSessionTracking(sessionID);
298 357
299 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Send message to session for group {0}", GroupID); 358 // If nessesary, remove from dropped list
359 if( m_agentsDroppedSession[sessionID].Contains(agentID) )
360 {
361 m_agentsDroppedSession[sessionID].Remove(agentID);
362 }
300 363
301 SendMessageToGroup(im, GroupID); 364 // If nessesary, add to in session list
365 if( !m_agentsInGroupSession[sessionID].Contains(agentID) )
366 {
367 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Added {1} to session {0}", sessionID, agentID);
368 m_agentsInGroupSession[sessionID].Add(agentID);
302 } 369 }
370 }
303 371
304 // Incoming message from a group 372 private void CreateGroupSessionTracking(Guid sessionID)
305 if ((im.dialog == (byte)InstantMessageDialog.SessionSend) && (im.fromGroup == true)) 373 {
374 if (!m_agentsInGroupSession.ContainsKey(sessionID))
306 { 375 {
307 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] Message from group session {0} going to agent {1}", im.fromAgentID, im.toAgentID); 376 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Creating session tracking for : {0}", sessionID);
377 m_agentsInGroupSession.Add(sessionID, new List<Guid>());
378 m_agentsDroppedSession.Add(sessionID, new List<Guid>());
308 } 379 }
309 } 380 }
310 #endregion
311 381
312 private void RegisterClientAgent(IClientAPI client) 382 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
313 { 383 {
314 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 384 if (m_debugEnabled)
385 {
386 m_log.DebugFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
315 387
316 lock (m_ActiveClients) 388 DebugGridInstantMessage(im);
389 }
390
391 // Start group IM session
392 if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart))
317 { 393 {
318 if (!m_ActiveClients.ContainsKey(client.AgentId.Guid)) 394 UUID groupID = new UUID(im.toAgentID);
395
396 GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID);
397 if (groupInfo != null)
319 { 398 {
320 client.OnInstantMessage += OnInstantMessage; 399 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Start Group Session for {0}", groupInfo.GroupName);
321 400
322 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] OnInstantMessage registered for {0}", client.Name); 401 AddAgentToGroupSession(im.fromAgentID, im.imSessionID);
323 402
324 m_ActiveClients.Add(client.AgentId.Guid, client); 403 ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, groupID);
325 }
326 else
327 {
328 // Remove old client connection for this agent
329 UnregisterClientAgent(client.AgentId);
330 404
331 // Add new client connection 405 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
332 RegisterClientAgent(client); 406 queue.ChatterBoxSessionAgentListUpdates(
407 new UUID(groupID)
408 , new UUID(im.fromAgentID)
409 , new UUID(im.toAgentID)
410 , false //canVoiceChat
411 , false //isModerator
412 , false //text mute
413 );
333 } 414 }
334 } 415 }
335 } 416
336 private void UnregisterClientAgent(UUID agentID) 417 // Send a message from locally connected client to a group
337 { 418 if ((im.dialog == (byte)InstantMessageDialog.SessionSend))
338 lock (m_ActiveClients)
339 { 419 {
340 if (m_ActiveClients.ContainsKey(agentID.Guid)) 420 UUID groupID = new UUID(im.toAgentID);
341 {
342 IClientAPI client = m_ActiveClients[agentID.Guid];
343 client.OnInstantMessage -= OnInstantMessage;
344 421
345 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] OnInstantMessage unregistered for {0}", client.Name); 422 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Send message to session for group {0} with session ID {1}", groupID, im.imSessionID.ToString());
346 423
347 m_ActiveClients.Remove(agentID.Guid); 424 SendMessageToGroup(im, groupID);
348 }
349 else
350 {
351 m_log.InfoFormat("[GROUPS-MESSAGING] Client closed that wasn't registered here.");
352 }
353 } 425 }
354 } 426 }
427 #endregion
355 428
356 private void SendMessageToGroup(GridInstantMessage im, UUID groupID) 429 private void SendMessageToGroup(GridInstantMessage im, UUID groupID)
357 { 430 {
358 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 431 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
432
359 433
434 foreach (GroupMembersData member in m_groupsModule.GroupMembersRequest(null, groupID))
435 {
436 if (m_agentsDroppedSession[im.imSessionID].Contains(member.AgentID.Guid))
437 {
438 // Don't deliver messages to people who have dropped this session
439 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] {0} has dropped session, not delivering to them", member.AgentID);
440 continue;
441 }
442
443 // Copy Message
360 GridInstantMessage msg = new GridInstantMessage(); 444 GridInstantMessage msg = new GridInstantMessage();
361 msg.imSessionID = im.imSessionID; 445 msg.imSessionID = im.imSessionID;
362 msg.fromAgentID = im.imSessionID; // GroupID
363 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
364 msg.fromAgentName = im.fromAgentName; 446 msg.fromAgentName = im.fromAgentName;
365 msg.message = im.message; 447 msg.message = im.message;
366 msg.dialog = im.dialog; 448 msg.dialog = im.dialog;
367 msg.fromGroup = true; 449 msg.offline = im.offline;
368 msg.offline = (byte)0;
369 msg.ParentEstateID = im.ParentEstateID; 450 msg.ParentEstateID = im.ParentEstateID;
370 msg.Position = im.Position; 451 msg.Position = im.Position;
371 msg.RegionID = im.RegionID; 452 msg.RegionID = im.RegionID;
372 msg.binaryBucket = new byte[1]{0}; 453 msg.binaryBucket = im.binaryBucket;
454 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
455
456 // Updat Pertinate fields to make it a "group message"
457 msg.fromAgentID = groupID.Guid;
458 msg.fromGroup = true;
373 459
374 foreach (GroupMembersData member in m_GroupsModule.GroupMembersRequest(null, groupID))
375 {
376 msg.toAgentID = member.AgentID.Guid; 460 msg.toAgentID = member.AgentID.Guid;
377 m_MsgTransferModule.SendInstantMessage(msg, delegate(bool success) {}); 461
462 IClientAPI client = GetActiveClient(member.AgentID);
463 if (client == null)
464 {
465 // If they're not local, forward across the grid
466 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Delivering to {0} via Grid", member.AgentID);
467 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { });
468 }
469 else
470 {
471 // Deliver locally, directly
472 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name);
473 ProcessMessageFromGroupSession(msg);
474 }
378 } 475 }
379 } 476 }
380 477
381 void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) 478 void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID)
382 { 479 {
383 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 480 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
384 481
385 OSDMap moderatedMap = new OSDMap(4); 482 OSDMap moderatedMap = new OSDMap(4);
386 moderatedMap.Add("voice", OSD.FromBoolean(false)); 483 moderatedMap.Add("voice", OSD.FromBoolean(false));
@@ -398,12 +495,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
398 bodyMap.Add("success", OSD.FromBoolean(true)); 495 bodyMap.Add("success", OSD.FromBoolean(true));
399 bodyMap.Add("session_info", sessionMap); 496 bodyMap.Add("session_info", sessionMap);
400 497
498
401 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>(); 499 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
402 500
403 if (queue != null) 501 if (queue != null)
404 { 502 {
405 queue.Enqueue(EventQueueHelper.buildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId); 503 queue.Enqueue(EventQueueHelper.buildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId);
406 } 504 }
505
407 } 506 }
408 507
409 508
@@ -422,5 +521,39 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
422 m_log.WarnFormat("[GROUPS-MESSAGING] IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket")); 521 m_log.WarnFormat("[GROUPS-MESSAGING] IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket"));
423 } 522 }
424 } 523 }
524
525 #region Client Tools
526
527 /// <summary>
528 /// Try to find an active IClientAPI reference for agentID giving preference to root connections
529 /// </summary>
530 private IClientAPI GetActiveClient(UUID agentID)
531 {
532 IClientAPI child = null;
533
534 // Try root avatar first
535 foreach (Scene scene in m_sceneList)
536 {
537 if (scene.Entities.ContainsKey(agentID) &&
538 scene.Entities[agentID] is ScenePresence)
539 {
540 ScenePresence user = (ScenePresence)scene.Entities[agentID];
541 if (!user.IsChildAgent)
542 {
543 return user.ControllingClient;
544 }
545 else
546 {
547 child = user.ControllingClient;
548 }
549 }
550 }
551
552 // If we didn't find a root, then just return whichever child we found, or null if none
553 return child;
554 }
555
556 #endregion
425 } 557 }
558
426} 559}
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs
index 69c7258..d5cd7e2 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsModule.cs
@@ -30,7 +30,6 @@ using System.Collections.Generic;
30using System.Reflection; 30using System.Reflection;
31 31
32using System.Collections; 32using System.Collections;
33//using Nwc.XmlRpc;
34 33
35using log4net; 34using log4net;
36using Nini.Config; 35using Nini.Config;
@@ -39,6 +38,7 @@ using OpenMetaverse;
39using OpenMetaverse.StructuredData; 38using OpenMetaverse.StructuredData;
40 39
41using OpenSim.Framework; 40using OpenSim.Framework;
41using OpenSim.Framework.Communications;
42using OpenSim.Region.CoreModules.Framework.EventQueue; 42using OpenSim.Region.CoreModules.Framework.EventQueue;
43using OpenSim.Region.Framework.Interfaces; 43using OpenSim.Region.Framework.Interfaces;
44using OpenSim.Region.Framework.Scenes; 44using OpenSim.Region.Framework.Scenes;
@@ -50,7 +50,7 @@ using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags;
50 50
51namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups 51namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
52{ 52{
53 public class XmlRpcGroupsModule : INonSharedRegionModule, IGroupsModule 53 public class XmlRpcGroupsModule : ISharedRegionModule, IGroupsModule
54 { 54 {
55 /// <summary> 55 /// <summary>
56 /// ; To use this module, you must specify the following in your OpenSim.ini 56 /// ; To use this module, you must specify the following in your OpenSim.ini
@@ -75,11 +75,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
75 75
76 private List<Scene> m_sceneList = new List<Scene>(); 76 private List<Scene> m_sceneList = new List<Scene>();
77 77
78 // This only works when running as non-Shared, in shared, there may be multiple IClientAPIs for a single client
79 private Dictionary<UUID, IClientAPI> m_activeClients = new Dictionary<UUID, IClientAPI>();
80
81 private IMessageTransferModule m_msgTransferModule = null; 78 private IMessageTransferModule m_msgTransferModule = null;
82 79
83 private IGroupDataProvider m_groupData = null; 80 private IGroupDataProvider m_groupData = null;
84 81
85 // Configuration settings 82 // Configuration settings
@@ -88,7 +85,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
88 private bool m_groupNoticesEnabled = true; 85 private bool m_groupNoticesEnabled = true;
89 private bool m_debugEnabled = true; 86 private bool m_debugEnabled = true;
90 87
91 #region IRegionModule Members 88 #region IRegionModuleBase Members
92 89
93 public void Initialise(IConfigSource config) 90 public void Initialise(IConfigSource config)
94 { 91 {
@@ -142,24 +139,34 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
142 if (!m_groupsEnabled) 139 if (!m_groupsEnabled)
143 return; 140 return;
144 141
145 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 142 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
146 143
147 m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
148
149 // No message transfer module, no notices, group invites, rejects, ejects, etc
150 if (m_msgTransferModule == null) 144 if (m_msgTransferModule == null)
151 { 145 {
152 m_groupsEnabled = false; 146 m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
153 m_log.Info("[GROUPS]: Could not get MessageTransferModule"); 147
154 Close(); 148 // No message transfer module, no notices, group invites, rejects, ejects, etc
155 return; 149 if (m_msgTransferModule == null)
150 {
151 m_groupsEnabled = false;
152 m_log.Error("[GROUPS]: Could not get MessageTransferModule");
153 Close();
154 return;
155 }
156 } 156 }
157 157
158 m_sceneList.Add(scene); 158 lock (m_sceneList)
159 {
160 m_sceneList.Add(scene);
161 }
159 162
160 scene.EventManager.OnNewClient += OnNewClient; 163 scene.EventManager.OnNewClient += OnNewClient;
161 scene.EventManager.OnClientClosed += OnClientClosed;
162 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; 164 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
165
166 // The InstantMessageModule itself doesn't do this,
167 // so lets see if things explode if we don't do it
168 // scene.EventManager.OnClientClosed += OnClientClosed;
169
163 } 170 }
164 171
165 public void RemoveRegion(Scene scene) 172 public void RemoveRegion(Scene scene)
@@ -167,117 +174,117 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
167 if (!m_groupsEnabled) 174 if (!m_groupsEnabled)
168 return; 175 return;
169 176
170 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 177 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
171 178
172 m_sceneList.Remove(scene); 179 lock (m_sceneList)
180 {
181 m_sceneList.Remove(scene);
182 }
173 } 183 }
174 184
175 public void Close() 185 public void Close()
176 { 186 {
177 if (!m_groupsEnabled) 187 if (!m_groupsEnabled)
178 return; 188 return;
179 m_log.Debug("[GROUPS]: Shutting down XmlRpcGroups module."); 189
190 if (m_debugEnabled) m_log.Debug("[GROUPS]: Shutting down XmlRpcGroups module.");
180 } 191 }
181 192
182 public string Name 193 public string Name
183 { 194 {
184 get { return "XmlRpcGroupsModule"; } 195 get { return "XmlRpcGroupsModule"; }
185 } 196 }
197
186 #endregion 198 #endregion
187 199
188 private void UpdateAllClientsWithGroupInfo() 200 #region ISharedRegionModule Members
189 {
190 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
191 foreach (IClientAPI client in m_activeClients.Values)
192 {
193 UpdateClientWithGroupInfo(client);
194 }
195 }
196 201
197 private void UpdateClientWithGroupInfo(IClientAPI client) 202 public void PostInitialise()
198 { 203 {
199 m_log.InfoFormat("[GROUPS] {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, client.Name); 204 // NoOp
200 OnAgentDataUpdateRequest(client, client.AgentId, UUID.Zero);
201
202
203 // Need to send a group membership update to the client
204 // UDP version doesn't seem to behave nicely
205 // client.SendGroupMembership(GetMembershipData(client.AgentId));
206
207 GroupMembershipData[] membershipData = m_groupData.GetAgentGroupMemberships(client.AgentId).ToArray();
208
209 SendGroupMembershipInfoViaCaps(client, membershipData);
210 client.SendAvatarGroupsReply(client.AgentId, membershipData);
211
212 } 205 }
213 206
207 #endregion
208
214 #region EventHandlers 209 #region EventHandlers
215 private void OnNewClient(IClientAPI client) 210 private void OnNewClient(IClientAPI client)
216 { 211 {
217 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 212 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
218 213
219 214 client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest;
220 lock (m_activeClients) 215 client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest;
221 { 216 client.OnDirFindQuery += OnDirFindQuery;
222 if (!m_activeClients.ContainsKey(client.AgentId)) 217 client.OnRequestAvatarProperties += OnRequestAvatarProperties;
223 {
224 client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest;
225 client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest;
226 client.OnDirFindQuery += OnDirFindQuery;
227 client.OnInstantMessage += OnInstantMessage;
228 218
229 m_activeClients.Add(client.AgentId, client); 219 // Used for Notices and Group Invites/Accept/Reject
230 } 220 client.OnInstantMessage += OnInstantMessage;
231 } 221
222 SendAgentGroupDataUpdate(client, client.AgentId);
223 }
232 224
233 UpdateClientWithGroupInfo(client); 225 private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID)
226 {
227 GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(avatarID).ToArray();
228 remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups);
234 } 229 }
235 230
236 private void OnClientClosed(UUID agentId) 231 /*
232 * This becomes very problematic in a shared module. In a shared module you may have more then one
233 * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections.
234 * The OnClientClosed event does not provide anything to indicate which one of those should be closed
235 * nor does it provide what scene it was from so that the specific reference can be looked up.
236 * The InstantMessageModule.cs does not currently worry about unregistering the handles,
237 * and it should be an issue, since it's the client that references us not the other way around
238 * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed
239 private void OnClientClosed(UUID AgentId)
237 { 240 {
238 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 241 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
239 242
240 lock (m_activeClients) 243 lock (m_ActiveClients)
241 { 244 {
242 if (m_activeClients.ContainsKey(agentId)) 245 if (m_ActiveClients.ContainsKey(AgentId))
243 { 246 {
244 IClientAPI client = m_activeClients[agentId]; 247 IClientAPI client = m_ActiveClients[AgentId];
245 client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest; 248 client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
246 client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest; 249 client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest;
247 client.OnDirFindQuery -= OnDirFindQuery; 250 client.OnDirFindQuery -= OnDirFindQuery;
248 client.OnInstantMessage -= OnInstantMessage; 251 client.OnInstantMessage -= OnInstantMessage;
249 252
250 m_activeClients.Remove(agentId); 253 m_ActiveClients.Remove(AgentId);
251 } 254 }
252 else 255 else
253 { 256 {
254 m_log.InfoFormat("[GROUPS] Client closed that wasn't registered here."); 257 if (m_debugEnabled) m_log.WarnFormat("[GROUPS] Client closed that wasn't registered here.");
255 } 258 }
259
260
256 } 261 }
257 } 262 }
263 */
258 264
259 265
260 void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart) 266 void OnDirFindQuery(IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart)
261 { 267 {
262 if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups) 268 if (((DirFindFlags)queryFlags & DirFindFlags.Groups) == DirFindFlags.Groups)
263 { 269 {
264 m_log.InfoFormat("[GROUPS] {0} called with queryText({1}) queryFlags({2}) queryStart({3})", 270 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called with queryText({1}) queryFlags({2}) queryStart({3})", System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart);
265 System.Reflection.MethodBase.GetCurrentMethod().Name, queryText, (DirFindFlags)queryFlags, queryStart);
266 271
272 // TODO: This currently ignores pretty much all the query flags including Mature and sort order
267 remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(queryText).ToArray()); 273 remoteClient.SendDirGroupsReply(queryID, m_groupData.FindGroups(queryText).ToArray());
268 } 274 }
275
269 } 276 }
270 277
271 private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID agentID, UUID sessionID) 278 private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
272 { 279 {
273 m_log.InfoFormat("[GROUPS] {0} called with SessionID :: {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, sessionID); 280 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
274 281
275 UUID activeGroupID = UUID.Zero; 282 UUID activeGroupID = UUID.Zero;
276 string activeGroupTitle = string.Empty; 283 string activeGroupTitle = string.Empty;
277 string activeGroupName = string.Empty; 284 string activeGroupName = string.Empty;
278 ulong activeGroupPowers = (ulong)GroupPowers.None; 285 ulong activeGroupPowers = (ulong)GroupPowers.None;
279 286
280 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(agentID); 287 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(dataForAgentID);
281 if (membership != null) 288 if (membership != null)
282 { 289 {
283 activeGroupID = membership.GroupID; 290 activeGroupID = membership.GroupID;
@@ -285,59 +292,44 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
285 activeGroupPowers = membership.GroupPowers; 292 activeGroupPowers = membership.GroupPowers;
286 } 293 }
287 294
288 string firstname, lastname; 295 SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle);
289 IClientAPI agent;
290 if (m_activeClients.TryGetValue(agentID, out agent))
291 {
292 firstname = agent.FirstName;
293 lastname = agent.LastName;
294 } else {
295 firstname = "Unknown";
296 lastname = "Unknown";
297 }
298
299 UpdateScenePresenceWithTitle(agentID, activeGroupTitle);
300 296
301 remoteClient.SendAgentDataUpdate(agentID, activeGroupID, firstname, 297 SendScenePresenceUpdate(dataForAgentID, activeGroupTitle);
302 lastname, activeGroupPowers, activeGroupName,
303 activeGroupTitle);
304 } 298 }
305 299
306 private void HandleUUIDGroupNameRequest(UUID groupID, IClientAPI remoteClient) 300 private void HandleUUIDGroupNameRequest(UUID GroupID,IClientAPI remote_client)
307 { 301 {
308 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 302 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
309 303
310 string groupName; 304 string GroupName;
311 305
312 GroupRecord group = m_groupData.GetGroupRecord(groupID, null); 306 GroupRecord group = m_groupData.GetGroupRecord(GroupID, null);
313 if (group != null) 307 if (group != null)
314 { 308 {
315 groupName = group.GroupName; 309 GroupName = group.GroupName;
316 } 310 }
317 else 311 else
318 { 312 {
319 groupName = "Unknown"; 313 GroupName = "Unknown";
320 } 314 }
321 315
322 remoteClient.SendGroupNameReply(groupID, groupName); 316
317 remote_client.SendGroupNameReply(GroupID, GroupName);
323 } 318 }
324 319
325 320
326 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) 321 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
327 { 322 {
328 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 323 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
329 324
330 325
331 // Group invitations 326 // Group invitations
332 if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || 327 if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
333 (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
334 { 328 {
335 m_log.WarnFormat("[GROUPS] Received an IIM for {0}.", ((InstantMessageDialog)im.dialog).ToString());
336
337 UUID inviteID = new UUID(im.imSessionID); 329 UUID inviteID = new UUID(im.imSessionID);
338 GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(inviteID); 330 GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(inviteID);
339 331
340 m_log.WarnFormat("[GROUPS] Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); 332 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID);
341 333
342 UUID fromAgentID = new UUID(im.fromAgentID); 334 UUID fromAgentID = new UUID(im.fromAgentID);
343 if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID)) 335 if ((inviteInfo != null) && (fromAgentID == inviteInfo.AgentID))
@@ -346,32 +338,32 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
346 // Accept 338 // Accept
347 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) 339 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept)
348 { 340 {
349 m_log.WarnFormat("[GROUPS] Received an accept invite notice."); 341 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] Received an accept invite notice.");
350 342
351 // and the sessionid is the role 343 // and the sessionid is the role
352 m_groupData.AddAgentToGroup(inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID); 344 m_groupData.AddAgentToGroup(inviteInfo.AgentID, inviteInfo.GroupID, inviteInfo.RoleID);
353 345
354 if (m_msgTransferModule != null) 346 GridInstantMessage msg = new GridInstantMessage();
355 { 347 msg.imSessionID = UUID.Zero.Guid;
356 GridInstantMessage msg = new GridInstantMessage(); 348 msg.fromAgentID = UUID.Zero.Guid;
357 msg.imSessionID = UUID.Zero.Guid; 349 msg.toAgentID = inviteInfo.AgentID.Guid;
358 msg.fromAgentID = UUID.Zero.Guid; 350 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
359 msg.toAgentID = inviteInfo.AgentID.Guid; 351 msg.fromAgentName = "Groups";
360 msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); 352 msg.message = string.Format("You have been added to the group.");
361 msg.fromAgentName = "Groups"; 353 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox;
362 msg.message = string.Format("You have been added to the group."); 354 msg.fromGroup = false;
363 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox; 355 msg.offline = (byte)0;
364 msg.fromGroup = false; 356 msg.ParentEstateID = 0;
365 msg.offline = (byte)0; 357 msg.Position = Vector3.Zero;
366 msg.ParentEstateID = 0; 358 msg.RegionID = UUID.Zero.Guid;
367 msg.Position = Vector3.Zero; 359 msg.binaryBucket = new byte[0];
368 msg.RegionID = UUID.Zero.Guid; 360
369 msg.binaryBucket = new byte[0]; 361 OutgoingInstantMessage(msg, inviteInfo.AgentID);
370
371 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { });
372 }
373 362
374 UpdateAllClientsWithGroupInfo(); 363 UpdateAllClientsWithGroupInfo(inviteInfo.AgentID);
364
365 // TODO: If the inviter is still online, they need an agent dataupdate
366 // and maybe group membership updates for the invitee
375 367
376 m_groupData.RemoveAgentToGroupInvite(inviteID); 368 m_groupData.RemoveAgentToGroupInvite(inviteID);
377 } 369 }
@@ -379,10 +371,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
379 // Reject 371 // Reject
380 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) 372 if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)
381 { 373 {
382 m_log.WarnFormat("[GROUPS] Received a reject invite notice."); 374 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] Received a reject invite notice.");
383 m_groupData.RemoveAgentToGroupInvite(inviteID); 375 m_groupData.RemoveAgentToGroupInvite(inviteID);
384 376
385 } 377 }
378
379
386 } 380 }
387 } 381 }
388 382
@@ -394,12 +388,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
394 return; 388 return;
395 } 389 }
396 390
397 UUID groupID = new UUID(im.toAgentID); 391 UUID GroupID = new UUID(im.toAgentID);
398 if (m_groupData.GetGroupRecord(groupID, null) != null) 392 if (m_groupData.GetGroupRecord(GroupID, null) != null)
399 { 393 {
400 UUID noticeID = UUID.Random(); 394 UUID NoticeID = UUID.Random();
401 string subject = im.message.Substring(0, im.message.IndexOf('|')); 395 string Subject = im.message.Substring(0, im.message.IndexOf('|'));
402 string message = im.message.Substring(subject.Length + 1); 396 string Message = im.message.Substring(Subject.Length + 1);
403 397
404 byte[] bucket; 398 byte[] bucket;
405 399
@@ -408,50 +402,56 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
408 bucket = new byte[19]; 402 bucket = new byte[19];
409 bucket[0] = 0; //dunno 403 bucket[0] = 0; //dunno
410 bucket[1] = 0; //dunno 404 bucket[1] = 0; //dunno
411 groupID.ToBytes(bucket, 2); 405 GroupID.ToBytes(bucket, 2);
412 bucket[18] = 0; //dunno 406 bucket[18] = 0; //dunno
413 } 407 }
414 else 408 else
415 { 409 {
416 string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); 410 string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
417 binBucket = binBucket.Remove(0, 14).Trim(); 411 binBucket = binBucket.Remove(0, 14).Trim();
418 m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket); 412 if (m_debugEnabled)
419
420 OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket);
421
422 foreach (string key in binBucketOSD.Keys)
423 { 413 {
424 m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString()); 414 m_log.WarnFormat("I don't understand a group notice binary bucket of: {0}", binBucket);
425 } 415
416 OSDMap binBucketOSD = (OSDMap)OSDParser.DeserializeLLSDXml(binBucket);
417
418 foreach (string key in binBucketOSD.Keys)
419 {
420 m_log.WarnFormat("{0}: {1}", key, binBucketOSD[key].ToString());
421 }
422 }
426 423
427 // treat as if no attachment 424 // treat as if no attachment
428 bucket = new byte[19]; 425 bucket = new byte[19];
429 bucket[0] = 0; //dunno 426 bucket[0] = 0; //dunno
430 bucket[1] = 0; //dunno 427 bucket[1] = 0; //dunno
431 groupID.ToBytes(bucket, 2); 428 GroupID.ToBytes(bucket, 2);
432 bucket[18] = 0; //dunno 429 bucket[18] = 0; //dunno
433 } 430 }
434 431
435 432
436 m_groupData.AddGroupNotice(groupID, noticeID, im.fromAgentName, subject, message, bucket); 433 m_groupData.AddGroupNotice(GroupID, NoticeID, im.fromAgentName, Subject, Message, bucket);
437 if (OnNewGroupNotice != null) 434 if (OnNewGroupNotice != null)
438 { 435 {
439 OnNewGroupNotice(groupID, noticeID); 436 OnNewGroupNotice(GroupID, NoticeID);
440 } 437 }
441 438
442 // Build notice IIM 439 // Build notice IIM
443 GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, noticeID, 440 GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
444 (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
445 441
446 // Send notice out to everyone that wants notices 442 // Send notice out to everyone that wants notices
447 foreach (GroupMembersData member in m_groupData.GetGroupMembers(groupID)) 443 foreach (GroupMembersData member in m_groupData.GetGroupMembers(GroupID))
448 { 444 {
449 if (member.AcceptNotices) 445 if (member.AcceptNotices)
450 { 446 {
451 msg.toAgentID = member.AgentID.Guid; 447 msg.toAgentID = member.AgentID.Guid;
452 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) {}); 448 OutgoingInstantMessage(msg, member.AgentID);
449
453 } 450 }
454 } 451 }
452
453
454
455 } 455 }
456 } 456 }
457 457
@@ -460,24 +460,28 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
460 // TODO:FIXME: Use a presense server of some kind to find out where the 460 // TODO:FIXME: Use a presense server of some kind to find out where the
461 // client actually is, and try contacting that region directly to notify them, 461 // client actually is, and try contacting that region directly to notify them,
462 // or provide the notification via xmlrpc update queue 462 // or provide the notification via xmlrpc update queue
463 if (im.dialog == 210) 463 if ((im.dialog == 210))
464 { 464 {
465 // This is sent from the region that the ejectee was ejected from 465 // This is sent from the region that the ejectee was ejected from
466 // if it's being delivered here, then the ejectee is here 466 // if it's being delivered here, then the ejectee is here
467 // so we need to send local updates to the agent. 467 // so we need to send local updates to the agent.
468 if (m_msgTransferModule != null)
469 {
470 im.dialog = (byte)InstantMessageDialog.MessageFromAgent;
471 m_msgTransferModule.SendInstantMessage(im, delegate(bool success) {});
472 }
473 468
474 UUID ejecteeID = new UUID(im.toAgentID); 469 UUID ejecteeID = new UUID(im.toAgentID);
475 UUID groupID = new UUID(im.toAgentID); 470
476 if (m_activeClients.ContainsKey(ejecteeID)) 471 im.dialog = (byte)InstantMessageDialog.MessageFromAgent;
472 OutgoingInstantMessage(im, ejecteeID);
473
474 IClientAPI ejectee = GetActiveClient(ejecteeID);
475 if (ejectee != null)
477 { 476 {
478 m_activeClients[ejecteeID].SendAgentDropGroup(groupID); 477 UUID groupID = new UUID(im.fromAgentID);
478 ejectee.SendAgentDropGroup(groupID);
479 } 479 }
480
480 } 481 }
482
483
484
481 } 485 }
482 486
483 private void OnGridInstantMessage(GridInstantMessage msg) 487 private void OnGridInstantMessage(GridInstantMessage msg)
@@ -487,6 +491,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
487 // Trigger the above event handler 491 // Trigger the above event handler
488 OnInstantMessage(null, msg); 492 OnInstantMessage(null, msg);
489 493
494
490 // If a message from a group arrives here, it may need to be forwarded to a local client 495 // If a message from a group arrives here, it may need to be forwarded to a local client
491 if (msg.fromGroup == true) 496 if (msg.fromGroup == true)
492 { 497 {
@@ -495,56 +500,40 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
495 case (byte)InstantMessageDialog.GroupInvitation: 500 case (byte)InstantMessageDialog.GroupInvitation:
496 case (byte)InstantMessageDialog.GroupNotice: 501 case (byte)InstantMessageDialog.GroupNotice:
497 UUID toAgentID = new UUID(msg.toAgentID); 502 UUID toAgentID = new UUID(msg.toAgentID);
498 if (m_activeClients.ContainsKey(toAgentID)) 503 IClientAPI localClient = GetActiveClient(toAgentID);
504 if( localClient != null )
499 { 505 {
500 m_activeClients[toAgentID].SendInstantMessage(msg); 506 localClient.SendInstantMessage(msg);
501 } 507 }
502 break; 508 break;
503 } 509 }
504 } 510 }
505 511
506 } 512 }
507 #endregion
508 513
509 private void UpdateScenePresenceWithTitle(UUID agentID, string title)
510 {
511 m_log.DebugFormat("[GROUPS] Updating scene title for {0} with title: {1}", agentID, title);
512
513 ScenePresence presence = null;
514 lock (m_sceneList)
515 {
516 foreach (Scene scene in m_sceneList)
517 {
518 presence = scene.GetScenePresence(agentID);
519 if (presence != null)
520 {
521 presence.Grouptitle = title;
522
523 // FixMe: Ter suggests a "Schedule" method that I can't find.
524 presence.SendFullUpdateToAllClients();
525 }
526 }
527 }
528 }
529 514
515 #endregion
530 516
531 #region IGroupsModule Members 517 #region IGroupsModule Members
532 518
533 public event NewGroupNotice OnNewGroupNotice; 519 public event NewGroupNotice OnNewGroupNotice;
534 520
535 public GroupRecord GetGroupRecord(UUID groupID) 521 public GroupRecord GetGroupRecord(UUID GroupID)
536 { 522 {
537 return m_groupData.GetGroupRecord(groupID, null); 523 return m_groupData.GetGroupRecord(GroupID, null);
538 } 524 }
539 525
540 public void ActivateGroup(IClientAPI remoteClient, UUID groupID) 526 public void ActivateGroup(IClientAPI remoteClient, UUID groupID)
541 { 527 {
542 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 528 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
543 529
544 m_groupData.SetAgentActiveGroup(remoteClient.AgentId, groupID); 530 m_groupData.SetAgentActiveGroup(remoteClient.AgentId, groupID);
545 531
546 // UpdateClientWithGroupInfo(remoteClient); 532 // Changing active group changes title, active powers, all kinds of things
547 UpdateAllClientsWithGroupInfo(); 533 // anyone who is in any region that can see this client, should probably be
534 // updated with new group info. At a minimum, they should get ScenePresence
535 // updated with new title.
536 UpdateAllClientsWithGroupInfo(remoteClient.AgentId);
548 } 537 }
549 538
550 /// <summary> 539 /// <summary>
@@ -552,7 +541,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
552 /// </summary> 541 /// </summary>
553 public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) 542 public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
554 { 543 {
555 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 544 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
556 545
557 List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(remoteClient.AgentId, groupID); 546 List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(remoteClient.AgentId, groupID);
558 GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(remoteClient.AgentId, groupID); 547 GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(remoteClient.AgentId, groupID);
@@ -576,49 +565,61 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
576 565
577 public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID) 566 public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
578 { 567 {
579 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 568 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
580 569
581 List<GroupMembersData> data = m_groupData.GetGroupMembers(groupID); 570 List<GroupMembersData> data = m_groupData.GetGroupMembers(groupID);
582 571 if (m_debugEnabled)
583 foreach (GroupMembersData member in data)
584 { 572 {
585 m_log.InfoFormat("[GROUPS] {0} {1}", member.AgentID, member.Title); 573 foreach (GroupMembersData member in data)
574 {
575 m_log.DebugFormat("[GROUPS] {0} {1}", member.AgentID, member.Title);
576 }
586 } 577 }
587 578
588 return data; 579 return data;
580
589 } 581 }
590 582
591 public List<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) 583 public List<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
592 { 584 {
593 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 585 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
594 586
595 List<GroupRolesData> data = m_groupData.GetGroupRoles(groupID); 587 List<GroupRolesData> data = m_groupData.GetGroupRoles(groupID);
596 588
597 foreach (GroupRolesData member in data) 589 if (m_debugEnabled)
598 { 590 {
599 m_log.InfoFormat("[GROUPS] {0} {1}", member.Title, member.Members); 591 foreach (GroupRolesData member in data)
592 {
593 m_log.DebugFormat("[GROUPS] {0} {1}", member.Title, member.Members);
594 }
600 } 595 }
601 596
602 return data; 597 return data;
598
603 } 599 }
604 600
605 public List<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) 601 public List<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
606 { 602 {
607 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 603 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
608 604
609 List<GroupRoleMembersData> data = m_groupData.GetGroupRoleMembers(groupID); 605 List<GroupRoleMembersData> data = m_groupData.GetGroupRoleMembers(groupID);
610 606
611 foreach (GroupRoleMembersData member in data) 607 if (m_debugEnabled)
612 { 608 {
613 m_log.InfoFormat("[GROUPS] Av: {0} Role: {1}", member.MemberID, member.RoleID); 609 foreach (GroupRoleMembersData member in data)
610 {
611 m_log.DebugFormat("[GROUPS] Av: {0} Role: {1}", member.MemberID, member.RoleID);
612 }
614 } 613 }
615 614
616 return data; 615 return data;
616
617
617 } 618 }
618 619
619 public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) 620 public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID)
620 { 621 {
621 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 622 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
622 623
623 GroupProfileData profile = new GroupProfileData(); 624 GroupProfileData profile = new GroupProfileData();
624 625
@@ -651,45 +652,40 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
651 return profile; 652 return profile;
652 } 653 }
653 654
654 public GroupMembershipData[] GetMembershipData(UUID userID) 655 public GroupMembershipData[] GetMembershipData(UUID agentID)
655 { 656 {
656 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 657 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
657 658
658 return m_groupData.GetAgentGroupMemberships(userID).ToArray(); 659 return m_groupData.GetAgentGroupMemberships(agentID).ToArray();
659 } 660 }
660 661
661 public GroupMembershipData GetMembershipData(UUID groupID, UUID userID) 662 public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
662 { 663 {
663 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 664 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
664 665
665 return m_groupData.GetAgentGroupMembership(userID, groupID); 666 return m_groupData.GetAgentGroupMembership(agentID, groupID);
666 } 667 }
667 668
668 public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, 669 public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
669 bool showInList, UUID insigniaID, int membershipFee,
670 bool openEnrollment, bool allowPublish, bool maturePublish)
671 { 670 {
672 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 671 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
673 672
674 // TODO: Security Check? 673 // TODO: Security Check?
675 674
676 m_groupData.UpdateGroup(groupID, charter, showInList, insigniaID, membershipFee, 675 m_groupData.UpdateGroup(groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
677 openEnrollment, allowPublish, maturePublish);
678 } 676 }
679 677
680 public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile) 678 public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile)
681 { 679 {
682 // TODO: Security Check? 680 // TODO: Security Check?
683 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 681 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
684 682
685 m_groupData.SetAgentGroupInfo(remoteClient.AgentId, groupID, acceptNotices, listInProfile); 683 m_groupData.SetAgentGroupInfo(remoteClient.AgentId, groupID, acceptNotices, listInProfile);
686 } 684 }
687 685
688 public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, 686 public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
689 bool showInList, UUID insigniaID, int membershipFee,
690 bool openEnrollment, bool allowPublish, bool maturePublish)
691 { 687 {
692 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 688 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
693 689
694 if (m_groupData.GetGroupRecord(UUID.Zero, name) != null) 690 if (m_groupData.GetGroupRecord(UUID.Zero, name) != null)
695 { 691 {
@@ -697,19 +693,19 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
697 return UUID.Zero; 693 return UUID.Zero;
698 } 694 }
699 695
700 UUID groupID = m_groupData.CreateGroup(name, charter, showInList, insigniaID, membershipFee, 696 UUID groupID = m_groupData.CreateGroup(name, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish, remoteClient.AgentId);
701 openEnrollment, allowPublish, maturePublish, remoteClient.AgentId);
702 697
703 remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly"); 698 remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly");
704 699
705 UpdateClientWithGroupInfo(remoteClient); 700 // Update the founder with new group information.
701 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
706 702
707 return groupID; 703 return groupID;
708 } 704 }
709 705
710 public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID) 706 public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID)
711 { 707 {
712 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 708 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
713 709
714 // ToDo: check if agent is a member of group and is allowed to see notices? 710 // ToDo: check if agent is a member of group and is allowed to see notices?
715 711
@@ -721,7 +717,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
721 /// </summary> 717 /// </summary>
722 public string GetGroupTitle(UUID avatarID) 718 public string GetGroupTitle(UUID avatarID)
723 { 719 {
724 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 720 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
725 721
726 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(avatarID); 722 GroupMembershipData membership = m_groupData.GetAgentActiveMembership(avatarID);
727 if (membership != null) 723 if (membership != null)
@@ -736,18 +732,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
736 /// </summary> 732 /// </summary>
737 public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID) 733 public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID)
738 { 734 {
739 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 735 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
740 736
741 m_groupData.SetAgentActiveGroupRole(remoteClient.AgentId, groupID, titleRoleID); 737 m_groupData.SetAgentActiveGroupRole(remoteClient.AgentId, groupID, titleRoleID);
742 738
743 UpdateAllClientsWithGroupInfo(); 739 // TODO: Not sure what all is needed here, but if the active group role change is for the group
740 // the client currently has set active, then we need to do a scene presence update too
741 // if (m_groupData.GetAgentActiveMembership(remoteClient.AgentId).GroupID == GroupID)
742
743 UpdateAllClientsWithGroupInfo(remoteClient.AgentId);
744 } 744 }
745 745
746 746
747 public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, 747 public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType)
748 string name, string description, string title, ulong powers, byte updateType)
749 { 748 {
750 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 749 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
751 750
752 // TODO: Security Checks? 751 // TODO: Security Checks?
753 752
@@ -774,12 +773,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
774 773
775 } 774 }
776 775
777 UpdateClientWithGroupInfo(remoteClient); 776 // TODO: This update really should send out updates for everyone in the role that just got changed.
777 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
778 } 778 }
779 779
780 public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) 780 public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes)
781 { 781 {
782 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 782 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
783 // Todo: Security check 783 // Todo: Security check
784 784
785 switch (changes) 785 switch (changes)
@@ -798,44 +798,45 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
798 m_log.ErrorFormat("[GROUPS] {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); 798 m_log.ErrorFormat("[GROUPS] {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes);
799 break; 799 break;
800 } 800 }
801 UpdateClientWithGroupInfo(remoteClient); 801
802 // TODO: This update really should send out updates for everyone in the role that just got changed.
803 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
802 } 804 }
803 805
804 public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) 806 public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID)
805 { 807 {
806 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 808 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
807 809
808 810
809 GroupNoticeInfo data = m_groupData.GetGroupNotice(groupNoticeID); 811 GroupNoticeInfo data = m_groupData.GetGroupNotice(groupNoticeID);
810 812
811 if (data != null) 813 if (data != null)
812 { 814 {
813 if (m_msgTransferModule != null) 815 GroupRecord groupInfo = m_groupData.GetGroupRecord(data.GroupID, null);
814 { 816
815 GridInstantMessage msg = new GridInstantMessage(); 817 GridInstantMessage msg = new GridInstantMessage();
816 msg.imSessionID = UUID.Zero.Guid; 818 msg.imSessionID = UUID.Zero.Guid;
817 msg.fromAgentID = data.GroupID.Guid; 819 msg.fromAgentID = data.GroupID.Guid;
818 msg.toAgentID = remoteClient.AgentId.Guid; 820 msg.toAgentID = remoteClient.AgentId.Guid;
819 msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); 821 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
820 msg.fromAgentName = "Group Notice From"; 822 msg.fromAgentName = "Group Notice : " + groupInfo == null ? "Unknown" : groupInfo.GroupName;
821 msg.message = data.noticeData.Subject + "|" + data.Message; 823 msg.message = data.noticeData.Subject + "|" + data.Message;
822 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested; 824 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNoticeRequested;
823 msg.fromGroup = true; 825 msg.fromGroup = true;
824 msg.offline = (byte)0; 826 msg.offline = (byte)0;
825 msg.ParentEstateID = 0; 827 msg.ParentEstateID = 0;
826 msg.Position = Vector3.Zero; 828 msg.Position = Vector3.Zero;
827 msg.RegionID = UUID.Zero.Guid; 829 msg.RegionID = UUID.Zero.Guid;
828 msg.binaryBucket = data.BinaryBucket; 830 msg.binaryBucket = data.BinaryBucket;
829 831
830 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); 832 OutgoingInstantMessage(msg, remoteClient.AgentId);
831 }
832 } 833 }
833 834
834 } 835 }
835 836
836 public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) 837 public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
837 { 838 {
838 m_log.WarnFormat("[GROUPS] {0} is probably not properly implemented", System.Reflection.MethodBase.GetCurrentMethod().Name); 839 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
839 840
840 GridInstantMessage msg = new GridInstantMessage(); 841 GridInstantMessage msg = new GridInstantMessage();
841 msg.imSessionID = UUID.Zero.Guid; 842 msg.imSessionID = UUID.Zero.Guid;
@@ -857,32 +858,43 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
857 msg.message = info.noticeData.Subject + "|" + info.Message; 858 msg.message = info.noticeData.Subject + "|" + info.Message;
858 msg.binaryBucket = info.BinaryBucket; 859 msg.binaryBucket = info.BinaryBucket;
859 } 860 }
861 else
862 {
863 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] Group Notice {0} not found, composing empty message.", groupNoticeID);
864 msg.fromAgentID = UUID.Zero.Guid;
865 msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ;
866 msg.fromAgentName = string.Empty;
867 msg.message = string.Empty;
868 msg.binaryBucket = new byte[0];
869 }
860 870
861 return msg; 871 return msg;
862 } 872 }
863 873
864 public void SendAgentGroupDataUpdate(IClientAPI remoteClient) 874 public void SendAgentGroupDataUpdate(IClientAPI remoteClient)
865 { 875 {
866 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 876 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
867 877
868 UpdateClientWithGroupInfo(remoteClient); 878 // Send agent information about his groups
879 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
869 } 880 }
870 881
871 public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) 882 public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID)
872 { 883 {
873 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 884 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
874 885
875 // Should check to see if OpenEnrollment, or if there's an outstanding invitation 886 // Should check to see if OpenEnrollment, or if there's an outstanding invitation
876 m_groupData.AddAgentToGroup(remoteClient.AgentId, groupID, UUID.Zero); 887 m_groupData.AddAgentToGroup(remoteClient.AgentId, groupID, UUID.Zero);
877 888
878 remoteClient.SendJoinGroupReply(groupID, true); 889 remoteClient.SendJoinGroupReply(groupID, true);
879 890
880 UpdateClientWithGroupInfo(remoteClient); 891 // Should this send updates to everyone in the group?
892 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
881 } 893 }
882 894
883 public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID) 895 public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID)
884 { 896 {
885 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 897 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
886 898
887 m_groupData.RemoveAgentFromGroup(remoteClient.AgentId, groupID); 899 m_groupData.RemoveAgentFromGroup(remoteClient.AgentId, groupID);
888 900
@@ -890,107 +902,97 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
890 902
891 remoteClient.SendAgentDropGroup(groupID); 903 remoteClient.SendAgentDropGroup(groupID);
892 904
893 UpdateClientWithGroupInfo(remoteClient); 905 // SL sends out notifcations to the group messaging session that the person has left
906 // Should this also update everyone who is in the group?
907 SendAgentGroupDataUpdate(remoteClient, remoteClient.AgentId);
894 } 908 }
895 909
896 public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID) 910 public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID)
897 { 911 {
898 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 912 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
899 913
900 // Todo: Security check? 914 // Todo: Security check?
901 m_groupData.RemoveAgentFromGroup(ejecteeID, groupID); 915 m_groupData.RemoveAgentFromGroup(ejecteeID, groupID);
902 916
903 remoteClient.SendEjectGroupMemberReply(remoteClient.AgentId, groupID, true); 917 remoteClient.SendEjectGroupMemberReply(remoteClient.AgentId, groupID, true);
904 918
905 if (m_msgTransferModule != null) 919 GroupRecord groupInfo = m_groupData.GetGroupRecord(groupID, null);
906 { 920 UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(ejecteeID);
907 GroupRecord groupInfo = m_groupData.GetGroupRecord(groupID, null);
908 UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(ejecteeID);
909 921
910 if ((groupInfo == null) || (userProfile == null)) 922 if ((groupInfo == null) || (userProfile == null))
911 { 923 {
912 return; 924 return;
913 } 925 }
914 926
915 927
916 // Send Message to Ejectee 928 // Send Message to Ejectee
917 GridInstantMessage msg = new GridInstantMessage(); 929 GridInstantMessage msg = new GridInstantMessage();
918 930
919 msg.imSessionID = UUID.Zero.Guid; 931 msg.imSessionID = UUID.Zero.Guid;
920 msg.fromAgentID = remoteClient.AgentId.Guid; 932 msg.fromAgentID = remoteClient.AgentId.Guid;
921 // msg.fromAgentID = info.GroupID; 933 // msg.fromAgentID = info.GroupID;
922 msg.toAgentID = ejecteeID.Guid; 934 msg.toAgentID = ejecteeID.Guid;
923 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); 935 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
924 msg.timestamp = 0; 936 msg.timestamp = 0;
925 msg.fromAgentName = remoteClient.Name; 937 msg.fromAgentName = remoteClient.Name;
926 msg.message = string.Format("You have been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName); 938 msg.message = string.Format("You have been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName);
927 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent; 939 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent;
928 msg.fromGroup = false; 940 msg.fromGroup = false;
929 msg.offline = (byte)0; 941 msg.offline = (byte)0;
930 msg.ParentEstateID = 0; 942 msg.ParentEstateID = 0;
931 msg.Position = Vector3.Zero; 943 msg.Position = Vector3.Zero;
932 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid; 944 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid;
933 msg.binaryBucket = new byte[0]; 945 msg.binaryBucket = new byte[0];
934 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) 946 OutgoingInstantMessage(msg, ejecteeID);
935 {
936 m_log.DebugFormat("[GROUPS] Message Sent Success: {0}",
937 success,ToString());
938 });
939 947
940 948
941 // Message to ejector 949 // Message to ejector
942 // Interop, received special 210 code for ejecting a group member 950 // Interop, received special 210 code for ejecting a group member
943 // this only works within the comms servers domain, and won't work hypergrid 951 // this only works within the comms servers domain, and won't work hypergrid
944 // TODO:FIXME: Use a presense server of some kind to find out where the 952 // TODO:FIXME: Use a presense server of some kind to find out where the
945 // client actually is, and try contacting that region directly to notify them, 953 // client actually is, and try contacting that region directly to notify them,
946 // or provide the notification via xmlrpc update queue 954 // or provide the notification via xmlrpc update queue
947 955
948 msg = new GridInstantMessage(); 956 msg = new GridInstantMessage();
949 msg.imSessionID = UUID.Zero.Guid; 957 msg.imSessionID = UUID.Zero.Guid;
950 msg.fromAgentID = remoteClient.AgentId.Guid; 958 msg.fromAgentID = remoteClient.AgentId.Guid;
951 msg.toAgentID = remoteClient.AgentId.Guid; 959 msg.toAgentID = remoteClient.AgentId.Guid;
952 msg.timestamp = 0; 960 msg.timestamp = 0;
953 msg.fromAgentName = remoteClient.Name; 961 msg.fromAgentName = remoteClient.Name;
954 if (userProfile != null) 962 if (userProfile != null)
955 { 963 {
956 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", 964 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName, userProfile.Name);
957 remoteClient.Name, groupInfo.GroupName, userProfile.Name);
958 }
959 else
960 {
961 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.",
962 remoteClient.Name, groupInfo.GroupName, "Unknown member");
963 }
964 msg.dialog = (byte)210; //interop
965 msg.fromGroup = false;
966 msg.offline = (byte)0;
967 msg.ParentEstateID = 0;
968 msg.Position = Vector3.Zero;
969 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid;
970 msg.binaryBucket = new byte[0];
971 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success)
972 {
973 m_log.DebugFormat("[GROUPS] Message Sent Success: {0}",
974 success, ToString());
975 });
976 } 965 }
966 else
967 {
968 msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", remoteClient.Name, groupInfo.GroupName, "Unknown member");
969 }
970 msg.dialog = (byte)210; //interop
971 msg.fromGroup = false;
972 msg.offline = (byte)0;
973 msg.ParentEstateID = 0;
974 msg.Position = Vector3.Zero;
975 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid;
976 msg.binaryBucket = new byte[0];
977 OutgoingInstantMessage(msg, remoteClient.AgentId);
977 978
978 UpdateAllClientsWithGroupInfo(); 979
980 // SL sends out messages to everyone in the group
981 // Who all should receive updates and what should they be updated with?
982 UpdateAllClientsWithGroupInfo(ejecteeID);
979 } 983 }
980 984
981 public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID) 985 public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID)
982 { 986 {
983 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 987 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
984 m_log.WarnFormat("[GROUPS] GID {0}, AID {1}, RID {2} ", groupID, invitedAgentID, roleID);
985 988
986 // Todo: Security check, probably also want to send some kind of notification 989 // Todo: Security check, probably also want to send some kind of notification
987 UUID inviteID = UUID.Random(); 990 UUID InviteID = UUID.Random();
988 m_log.WarnFormat("[GROUPS] Invite ID: {0}", inviteID); 991 m_groupData.AddAgentToGroupInvite(InviteID, groupID, roleID, invitedAgentID);
989 m_groupData.AddAgentToGroupInvite(inviteID, groupID, roleID, invitedAgentID);
990 992
991 if (m_msgTransferModule != null) 993 if (m_msgTransferModule != null)
992 { 994 {
993 Guid inviteUUID = inviteID.Guid; 995 Guid inviteUUID = InviteID.Guid;
994 996
995 GridInstantMessage msg = new GridInstantMessage(); 997 GridInstantMessage msg = new GridInstantMessage();
996 998
@@ -1002,8 +1004,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1002 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); 1004 //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
1003 msg.timestamp = 0; 1005 msg.timestamp = 0;
1004 msg.fromAgentName = remoteClient.Name; 1006 msg.fromAgentName = remoteClient.Name;
1005 msg.message = string.Format("{0} has invited you to join a group. There is no cost to join this group.", 1007 msg.message = string.Format("{0} has invited you to join a group. There is no cost to join this group.", remoteClient.Name);
1006 remoteClient.Name);
1007 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; 1008 msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
1008 msg.fromGroup = true; 1009 msg.fromGroup = true;
1009 msg.offline = (byte)0; 1010 msg.offline = (byte)0;
@@ -1011,60 +1012,198 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
1011 msg.Position = Vector3.Zero; 1012 msg.Position = Vector3.Zero;
1012 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid; 1013 msg.RegionID = remoteClient.Scene.RegionInfo.RegionID.Guid;
1013 msg.binaryBucket = new byte[20]; 1014 msg.binaryBucket = new byte[20];
1014 1015
1015 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) 1016 OutgoingInstantMessage(msg, invitedAgentID);
1016 {
1017 m_log.DebugFormat("[GROUPS] Message Sent Success: {0}", success,ToString());
1018 });
1019 } 1017 }
1020 } 1018 }
1021 1019
1022 #endregion 1020 #endregion
1023 1021
1024 void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, GroupMembershipData[] data) 1022 #region Client/Update Tools
1023
1024 /// <summary>
1025 /// Try to find an active IClientAPI reference for agentID giving preference to root connections
1026 /// </summary>
1027 private IClientAPI GetActiveClient(UUID agentID)
1028 {
1029 IClientAPI child = null;
1030
1031 // Try root avatar first
1032 foreach (Scene scene in m_sceneList)
1033 {
1034 if (scene.Entities.ContainsKey(agentID) &&
1035 scene.Entities[agentID] is ScenePresence)
1036 {
1037 ScenePresence user = (ScenePresence)scene.Entities[agentID];
1038 if (!user.IsChildAgent)
1039 {
1040 return user.ControllingClient;
1041 }
1042 else
1043 {
1044 child = user.ControllingClient;
1045 }
1046 }
1047 }
1048
1049 // If we didn't find a root, then just return whichever child we found, or null if none
1050 return child;
1051 }
1052
1053 /// <summary>
1054 /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'.
1055 /// </summary>
1056 private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data)
1025 { 1057 {
1026 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); 1058 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1027 1059
1028 OSDArray agentData = new OSDArray(1); 1060 OSDArray AgentData = new OSDArray(1);
1029 OSDMap agentDataMap = new OSDMap(1); 1061 OSDMap AgentDataMap = new OSDMap(1);
1030 agentDataMap.Add("AgentID", OSD.FromUUID(remoteClient.AgentId)); 1062 AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID));
1031 agentData.Add(agentDataMap); 1063 AgentData.Add(AgentDataMap);
1032 1064
1033 1065
1034 OSDArray groupData = new OSDArray(data.Length); 1066 OSDArray GroupData = new OSDArray(data.Length);
1035 OSDArray newGroupData = new OSDArray(data.Length); 1067 OSDArray NewGroupData = new OSDArray(data.Length);
1036 1068
1037 foreach (GroupMembershipData membership in data) 1069 foreach (GroupMembershipData membership in data)
1038 { 1070 {
1039 OSDMap groupDataMap = new OSDMap(6); 1071 OSDMap GroupDataMap = new OSDMap(6);
1040 OSDMap newGroupDataMap = new OSDMap(1); 1072 OSDMap NewGroupDataMap = new OSDMap(1);
1041 1073
1042 groupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID)); 1074 GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID));
1043 groupDataMap.Add("GroupPowers", OSD.FromBinary(membership.GroupPowers)); 1075 GroupDataMap.Add("GroupPowers", OSD.FromBinary(membership.GroupPowers));
1044 groupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices)); 1076 GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices));
1045 groupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture)); 1077 GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture));
1046 groupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution)); 1078 GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution));
1047 groupDataMap.Add("GroupName", OSD.FromString(membership.GroupName)); 1079 GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName));
1048 newGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile)); 1080 NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile));
1049 1081
1050 groupData.Add(groupDataMap); 1082 GroupData.Add(GroupDataMap);
1051 newGroupData.Add(newGroupDataMap); 1083 NewGroupData.Add(NewGroupDataMap);
1052 } 1084 }
1053 1085
1054 OSDMap llDataStruct = new OSDMap(3); 1086 OSDMap llDataStruct = new OSDMap(3);
1055 llDataStruct.Add("AgentData", agentData); 1087 llDataStruct.Add("AgentData", AgentData);
1056 llDataStruct.Add("GroupData", groupData); 1088 llDataStruct.Add("GroupData", GroupData);
1057 llDataStruct.Add("NewGroupData", newGroupData); 1089 llDataStruct.Add("NewGroupData", NewGroupData);
1058 1090
1059 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>(); 1091 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
1060 1092
1061 if (queue != null) 1093 if (queue != null)
1062 { 1094 {
1063 queue.Enqueue(EventQueueHelper.buildEvent("AgentGroupDataUpdate", llDataStruct), 1095 queue.Enqueue(EventQueueHelper.buildEvent("AgentGroupDataUpdate", llDataStruct), remoteClient.AgentId);
1064 remoteClient.AgentId);
1065 } 1096 }
1066 1097
1067 } 1098 }
1099
1100 private void SendScenePresenceUpdate(UUID AgentID, string Title)
1101 {
1102 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] Updating scene title for {0} with title: {1}", AgentID, Title);
1103
1104 ScenePresence presence = null;
1105 lock (m_sceneList)
1106 {
1107 foreach (Scene scene in m_sceneList)
1108 {
1109 presence = scene.GetScenePresence(AgentID);
1110 if (presence != null)
1111 {
1112 presence.Grouptitle = Title;
1113
1114 // FixMe: Ter suggests a "Schedule" method that I can't find.
1115 presence.SendFullUpdateToAllClients();
1116 }
1117 }
1118 }
1119 }
1120
1121 /// <summary>
1122 /// Send updates to all clients who might be interested in groups data for dataForClientID
1123 /// </summary>
1124 private void UpdateAllClientsWithGroupInfo(UUID dataForClientID)
1125 {
1126 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1127
1128 // TODO: Probably isn't nessesary to update every client in every scene.
1129 // Need to examine client updates and do only what's nessesary.
1130 lock (m_sceneList)
1131 {
1132 foreach (Scene scene in m_sceneList)
1133 {
1134 scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); });
1135 }
1136 }
1137 }
1138
1139 /// <summary>
1140 /// Update remoteClient with group information about dataForAgentID
1141 /// </summary>
1142 private void SendAgentGroupDataUpdate(IClientAPI client, UUID dataForAgentID)
1143 {
1144 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, client.Name);
1145
1146 // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
1147
1148 OnAgentDataUpdateRequest(client, dataForAgentID, UUID.Zero);
1149
1150
1151 // Need to send a group membership update to the client
1152 // UDP version doesn't seem to behave nicely
1153 // client.SendGroupMembership(GetMembershipData(client.AgentId));
1154
1155 GroupMembershipData[] membershipData = m_groupData.GetAgentGroupMemberships(dataForAgentID).ToArray();
1156
1157 SendGroupMembershipInfoViaCaps(client, dataForAgentID, membershipData);
1158 client.SendAvatarGroupsReply(dataForAgentID, membershipData);
1159
1160 }
1161
1162 private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle)
1163 {
1164 if (m_debugEnabled) m_log.DebugFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1165
1166 // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
1167 UserProfileData userProfile = m_sceneList[0].CommsManager.UserService.GetUserProfile(dataForAgentID);
1168 string firstname, lastname;
1169 if (userProfile != null)
1170 {
1171 firstname = userProfile.FirstName;
1172 lastname = userProfile.SurName;
1173 }
1174 else
1175 {
1176 firstname = "Unknown";
1177 lastname = "Unknown";
1178 }
1179
1180 remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname,
1181 lastname, activeGroupPowers, activeGroupName,
1182 activeGroupTitle);
1183 }
1184
1185 #endregion
1186
1187 #region IM Backed Processes
1188
1189 private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo)
1190 {
1191 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
1192
1193 IClientAPI localClient = GetActiveClient(msgTo);
1194 if (localClient != null)
1195 {
1196 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] MsgTo ({0}) is local, delivering directly", localClient.Name);
1197 localClient.SendInstantMessage(msg);
1198 }
1199 else
1200 {
1201 if (m_debugEnabled) m_log.InfoFormat("[GROUPS] MsgTo ({0}) is not local, delivering via TransferModule", msgTo);
1202 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS] Message Sent: {0}", success?"Succeeded":"Failed"); });
1203 }
1204 }
1205
1206 #endregion
1068 } 1207 }
1069 1208
1070} 1209}