GroupsModule.cs 57 KB

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