GroupsModule.cs 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468
  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.Region.Framework.Interfaces;
  38. using OpenSim.Region.Framework.Scenes;
  39. using OpenSim.Services.Interfaces;
  40. using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags;
  41. namespace OpenSim.Groups
  42. {
  43. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsModule")]
  44. public class GroupsModule : ISharedRegionModule, IGroupsModule
  45. {
  46. /// <summary>
  47. /// </summary>
  48. private static readonly ILog m_log =
  49. LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  50. private List<Scene> m_sceneList = new List<Scene>();
  51. private IMessageTransferModule m_msgTransferModule = null;
  52. private IGroupsServicesConnector m_groupData = null;
  53. private IUserManagement m_UserManagement;
  54. // Configuration settings
  55. private bool m_groupsEnabled = false;
  56. private bool m_groupNoticesEnabled = true;
  57. private bool m_debugEnabled = false;
  58. private int m_levelGroupCreate = 0;
  59. #region Region Module interfaceBase Members
  60. public void Initialise(IConfigSource config)
  61. {
  62. IConfig groupsConfig = config.Configs["Groups"];
  63. if (groupsConfig == null)
  64. {
  65. // Do not run this module by default.
  66. return;
  67. }
  68. else
  69. {
  70. m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false);
  71. if (!m_groupsEnabled)
  72. {
  73. return;
  74. }
  75. if (groupsConfig.GetString("Module", "Default") != Name)
  76. {
  77. m_groupsEnabled = false;
  78. return;
  79. }
  80. m_log.InfoFormat("[Groups]: Initializing {0}", this.Name);
  81. m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true);
  82. m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false);
  83. m_levelGroupCreate = groupsConfig.GetInt("LevelGroupCreate", 0);
  84. }
  85. }
  86. public void AddRegion(Scene scene)
  87. {
  88. if (m_groupsEnabled)
  89. {
  90. scene.RegisterModuleInterface<IGroupsModule>(this);
  91. scene.AddCommand(
  92. "debug",
  93. this,
  94. "debug groups verbose",
  95. "debug groups verbose <true|false>",
  96. "This setting turns on very verbose groups debugging",
  97. HandleDebugGroupsVerbose);
  98. }
  99. }
  100. private void HandleDebugGroupsVerbose(object modules, string[] args)
  101. {
  102. if (args.Length < 4)
  103. {
  104. MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
  105. return;
  106. }
  107. bool verbose = false;
  108. if (!bool.TryParse(args[3], out verbose))
  109. {
  110. MainConsole.Instance.Output("Usage: debug groups verbose <true|false>");
  111. return;
  112. }
  113. m_debugEnabled = verbose;
  114. MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
  115. }
  116. public void RegionLoaded(Scene scene)
  117. {
  118. if (!m_groupsEnabled)
  119. return;
  120. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  121. scene.EventManager.OnNewClient += OnNewClient;
  122. scene.EventManager.OnMakeRootAgent += OnMakeRoot;
  123. scene.EventManager.OnMakeChildAgent += OnMakeChild;
  124. scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
  125. // The InstantMessageModule itself doesn't do this,
  126. // so lets see if things explode if we don't do it
  127. // scene.EventManager.OnClientClosed += OnClientClosed;
  128. if (m_groupData == null)
  129. {
  130. m_groupData = scene.RequestModuleInterface<IGroupsServicesConnector>();
  131. // No Groups Service Connector, then nothing works...
  132. if (m_groupData == null)
  133. {
  134. m_groupsEnabled = false;
  135. m_log.Error("[Groups]: Could not get IGroupsServicesConnector");
  136. RemoveRegion(scene);
  137. return;
  138. }
  139. }
  140. if (m_msgTransferModule == null)
  141. {
  142. m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
  143. // No message transfer module, no notices, group invites, rejects, ejects, etc
  144. if (m_msgTransferModule == null)
  145. {
  146. m_log.Warn("[Groups]: Could not get MessageTransferModule");
  147. }
  148. }
  149. if (m_UserManagement == null)
  150. {
  151. m_UserManagement = scene.RequestModuleInterface<IUserManagement>();
  152. if (m_UserManagement == null)
  153. m_log.Warn("[Groups]: Could not get UserManagementModule");
  154. }
  155. lock (m_sceneList)
  156. {
  157. m_sceneList.Add(scene);
  158. }
  159. }
  160. public void RemoveRegion(Scene scene)
  161. {
  162. if (!m_groupsEnabled)
  163. return;
  164. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  165. scene.EventManager.OnNewClient -= OnNewClient;
  166. scene.EventManager.OnMakeRootAgent -= OnMakeRoot;
  167. scene.EventManager.OnMakeChildAgent -= OnMakeChild;
  168. scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
  169. lock (m_sceneList)
  170. {
  171. m_sceneList.Remove(scene);
  172. }
  173. }
  174. public void Close()
  175. {
  176. if (!m_groupsEnabled)
  177. return;
  178. if (m_debugEnabled) m_log.Debug("[Groups]: Shutting down Groups module.");
  179. }
  180. public Type ReplaceableInterface
  181. {
  182. get { return null; }
  183. }
  184. public string Name
  185. {
  186. get { return "Groups Module V2"; }
  187. }
  188. public void PostInitialise()
  189. {
  190. // NoOp
  191. }
  192. #endregion
  193. #region EventHandlers
  194. private void OnNewClient(IClientAPI client)
  195. {
  196. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  197. client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest;
  198. client.OnRequestAvatarProperties += OnRequestAvatarProperties;
  199. }
  200. private void OnMakeRoot(ScenePresence sp)
  201. {
  202. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  203. sp.ControllingClient.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest;
  204. // Used for Notices and Group Invites/Accept/Reject
  205. sp.ControllingClient.OnInstantMessage += OnInstantMessage;
  206. // Send client their groups information.
  207. SendAgentGroupDataUpdate(sp.ControllingClient, sp.UUID);
  208. }
  209. private void OnMakeChild(ScenePresence sp)
  210. {
  211. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  212. sp.ControllingClient.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
  213. // Used for Notices and Group Invites/Accept/Reject
  214. sp.ControllingClient.OnInstantMessage -= OnInstantMessage;
  215. }
  216. private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID)
  217. {
  218. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  219. //GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetRequestingAgentID(remoteClient), avatarID).ToArray();
  220. GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID);
  221. remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups);
  222. }
  223. /*
  224. * This becomes very problematic in a shared module. In a shared module you may have more then one
  225. * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections.
  226. * The OnClientClosed event does not provide anything to indicate which one of those should be closed
  227. * nor does it provide what scene it was from so that the specific reference can be looked up.
  228. * The InstantMessageModule.cs does not currently worry about unregistering the handles,
  229. * and it should be an issue, since it's the client that references us not the other way around
  230. * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed
  231. private void OnClientClosed(UUID AgentId)
  232. {
  233. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  234. lock (m_ActiveClients)
  235. {
  236. if (m_ActiveClients.ContainsKey(AgentId))
  237. {
  238. IClientAPI client = m_ActiveClients[AgentId];
  239. client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest;
  240. client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest;
  241. client.OnDirFindQuery -= OnDirFindQuery;
  242. client.OnInstantMessage -= OnInstantMessage;
  243. m_ActiveClients.Remove(AgentId);
  244. }
  245. else
  246. {
  247. if (m_debugEnabled) m_log.WarnFormat("[Groups]: Client closed that wasn't registered here.");
  248. }
  249. }
  250. }
  251. */
  252. private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID)
  253. {
  254. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  255. UUID activeGroupID = UUID.Zero;
  256. string activeGroupTitle = string.Empty;
  257. string activeGroupName = string.Empty;
  258. ulong activeGroupPowers = (ulong)GroupPowers.None;
  259. GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetRequestingAgentIDStr(remoteClient), dataForAgentID.ToString());
  260. if (membership != null)
  261. {
  262. activeGroupID = membership.GroupID;
  263. activeGroupTitle = membership.GroupTitle;
  264. activeGroupPowers = membership.GroupPowers;
  265. }
  266. SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle);
  267. SendScenePresenceUpdate(dataForAgentID, activeGroupTitle);
  268. }
  269. private void HandleUUIDGroupNameRequest(UUID GroupID, IClientAPI remoteClient)
  270. {
  271. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  272. string GroupName;
  273. GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null);
  274. if (group != null)
  275. {
  276. GroupName = group.GroupName;
  277. }
  278. else
  279. {
  280. GroupName = "Unknown";
  281. }
  282. remoteClient.SendGroupNameReply(GroupID, GroupName);
  283. }
  284. private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
  285. {
  286. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  287. //m_log.DebugFormat("[Groups]: IM From {0} to {1} msg {2} type {3}", im.fromAgentID, im.toAgentID, im.message, (InstantMessageDialog)im.dialog);
  288. // Group invitations
  289. if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline))
  290. {
  291. UUID inviteID = new UUID(im.imSessionID);
  292. GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID);
  293. if (inviteInfo == null)
  294. {
  295. if (m_debugEnabled) m_log.WarnFormat("[Groups]: Received an Invite IM for an invite that does not exist {0}.", inviteID);
  296. return;
  297. }
  298. //m_log.DebugFormat("[XXX]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID);
  299. UUID fromAgentID = new UUID(im.fromAgentID);
  300. UUID invitee = UUID.Zero;
  301. string tmp = string.Empty;
  302. Util.ParseUniversalUserIdentifier(inviteInfo.AgentID, out invitee, out tmp, out tmp, out tmp, out tmp);
  303. if ((inviteInfo != null) && (fromAgentID == invitee))
  304. {
  305. // Accept
  306. if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept)
  307. {
  308. //m_log.DebugFormat("[XXX]: Received an accept invite notice.");
  309. // and the sessionid is the role
  310. string reason = string.Empty;
  311. if (!m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), invitee.ToString(), inviteInfo.GroupID, inviteInfo.RoleID, string.Empty, out reason))
  312. remoteClient.SendAgentAlertMessage("Unable to add you to the group: " + reason, false);
  313. else
  314. {
  315. GridInstantMessage msg = new GridInstantMessage();
  316. msg.imSessionID = UUID.Zero.Guid;
  317. msg.fromAgentID = UUID.Zero.Guid;
  318. msg.toAgentID = invitee.Guid;
  319. msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
  320. msg.fromAgentName = "Groups";
  321. msg.message = string.Format("You have been added to the group.");
  322. msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox;
  323. msg.fromGroup = false;
  324. msg.offline = (byte)0;
  325. msg.ParentEstateID = 0;
  326. msg.Position = Vector3.Zero;
  327. msg.RegionID = UUID.Zero.Guid;
  328. msg.binaryBucket = new byte[0];
  329. OutgoingInstantMessage(msg, invitee);
  330. UpdateAllClientsWithGroupInfo(invitee);
  331. }
  332. m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID);
  333. }
  334. // Reject
  335. if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)
  336. {
  337. if (m_debugEnabled) m_log.DebugFormat("[Groups]: Received a reject invite notice.");
  338. m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID);
  339. m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID);
  340. }
  341. }
  342. }
  343. // Group notices
  344. if ((im.dialog == (byte)InstantMessageDialog.GroupNotice))
  345. {
  346. if (!m_groupNoticesEnabled)
  347. {
  348. return;
  349. }
  350. UUID GroupID = new UUID(im.toAgentID);
  351. if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null) != null)
  352. {
  353. UUID NoticeID = UUID.Random();
  354. string Subject = im.message.Substring(0, im.message.IndexOf('|'));
  355. string Message = im.message.Substring(Subject.Length + 1);
  356. InventoryItemBase item = null;
  357. bool hasAttachment = false;
  358. if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0)
  359. {
  360. hasAttachment = true;
  361. string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket);
  362. binBucket = binBucket.Remove(0, 14).Trim();
  363. OSD binBucketOSD = OSDParser.DeserializeLLSDXml(binBucket);
  364. if (binBucketOSD is OSDMap)
  365. {
  366. OSDMap binBucketMap = (OSDMap)binBucketOSD;
  367. UUID itemID = binBucketMap["item_id"].AsUUID();
  368. UUID ownerID = binBucketMap["owner_id"].AsUUID();
  369. item = new InventoryItemBase(itemID, ownerID);
  370. item = m_sceneList[0].InventoryService.GetItem(item);
  371. }
  372. else
  373. m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType());
  374. }
  375. if (m_groupData.AddGroupNotice(GetRequestingAgentIDStr(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message,
  376. hasAttachment,
  377. (byte)(item == null ? 0 : item.AssetType),
  378. item == null ? null : item.Name,
  379. item == null ? UUID.Zero : item.ID,
  380. item == null ? UUID.Zero.ToString() : item.Owner.ToString()))
  381. {
  382. if (OnNewGroupNotice != null)
  383. {
  384. OnNewGroupNotice(GroupID, NoticeID);
  385. }
  386. // Send notice out to everyone that wants notices
  387. foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), GroupID))
  388. {
  389. if (member.AcceptNotices)
  390. {
  391. // Build notice IIM, one of reach, because the sending may be async
  392. GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice);
  393. msg.toAgentID = member.AgentID.Guid;
  394. OutgoingInstantMessage(msg, member.AgentID);
  395. }
  396. }
  397. }
  398. }
  399. }
  400. if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted)
  401. {
  402. if (im.binaryBucket.Length < 16) // Invalid
  403. return;
  404. //// 16 bytes are the UUID. Maybe.
  405. // UUID folderID = new UUID(im.binaryBucket, 0);
  406. UUID noticeID = new UUID(im.imSessionID);
  407. GroupNoticeInfo notice = m_groupData.GetGroupNotice(remoteClient.AgentId.ToString(), noticeID);
  408. if (notice != null)
  409. {
  410. UUID giver = new UUID(im.toAgentID);
  411. string tmp = string.Empty;
  412. Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out giver, out tmp, out tmp, out tmp, out tmp);
  413. m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId);
  414. InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId,
  415. giver, notice.noticeData.AttachmentItemID);
  416. if (itemCopy == null)
  417. {
  418. remoteClient.SendAgentAlertMessage("Can't find item to give. Nothing given.", false);
  419. return;
  420. }
  421. remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0);
  422. }
  423. }
  424. // Interop, received special 210 code for ejecting a group member
  425. // this only works within the comms servers domain, and won't work hypergrid
  426. // TODO:FIXME: Use a presense server of some kind to find out where the
  427. // client actually is, and try contacting that region directly to notify them,
  428. // or provide the notification via xmlrpc update queue
  429. if ((im.dialog == 210))
  430. {
  431. // This is sent from the region that the ejectee was ejected from
  432. // if it's being delivered here, then the ejectee is here
  433. // so we need to send local updates to the agent.
  434. UUID ejecteeID = new UUID(im.toAgentID);
  435. im.dialog = (byte)InstantMessageDialog.MessageFromAgent;
  436. OutgoingInstantMessage(im, ejecteeID);
  437. IClientAPI ejectee = GetActiveClient(ejecteeID);
  438. if (ejectee != null)
  439. {
  440. UUID groupID = new UUID(im.imSessionID);
  441. ejectee.SendAgentDropGroup(groupID);
  442. }
  443. }
  444. }
  445. private void OnGridInstantMessage(GridInstantMessage msg)
  446. {
  447. if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  448. // Trigger the above event handler
  449. OnInstantMessage(null, msg);
  450. // If a message from a group arrives here, it may need to be forwarded to a local client
  451. if (msg.fromGroup == true)
  452. {
  453. switch (msg.dialog)
  454. {
  455. case (byte)InstantMessageDialog.GroupInvitation:
  456. case (byte)InstantMessageDialog.GroupNotice:
  457. UUID toAgentID = new UUID(msg.toAgentID);
  458. IClientAPI localClient = GetActiveClient(toAgentID);
  459. if (localClient != null)
  460. {
  461. localClient.SendInstantMessage(msg);
  462. }
  463. break;
  464. }
  465. }
  466. }
  467. #endregion
  468. #region IGroupsModule Members
  469. public event NewGroupNotice OnNewGroupNotice;
  470. public GroupRecord GetGroupRecord(UUID GroupID)
  471. {
  472. return m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
  473. }
  474. public GroupRecord GetGroupRecord(string name)
  475. {
  476. return m_groupData.GetGroupRecord(UUID.Zero.ToString(), UUID.Zero, name);
  477. }
  478. public void ActivateGroup(IClientAPI remoteClient, UUID groupID)
  479. {
  480. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  481. m_groupData.SetAgentActiveGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
  482. // Changing active group changes title, active powers, all kinds of things
  483. // anyone who is in any region that can see this client, should probably be
  484. // updated with new group info. At a minimum, they should get ScenePresence
  485. // updated with new title.
  486. UpdateAllClientsWithGroupInfo(remoteClient.AgentId);
  487. }
  488. /// <summary>
  489. /// Get the Role Titles for an Agent, for a specific group
  490. /// </summary>
  491. public List<GroupTitlesData> GroupTitlesRequest(IClientAPI remoteClient, UUID groupID)
  492. {
  493. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  494. List<GroupRolesData> agentRoles = m_groupData.GetAgentGroupRoles(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
  495. GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
  496. List<GroupTitlesData> titles = new List<GroupTitlesData>();
  497. foreach (GroupRolesData role in agentRoles)
  498. {
  499. GroupTitlesData title = new GroupTitlesData();
  500. title.Name = role.Name;
  501. if (agentMembership != null)
  502. {
  503. title.Selected = agentMembership.ActiveRole == role.RoleID;
  504. }
  505. title.UUID = role.RoleID;
  506. titles.Add(title);
  507. }
  508. return titles;
  509. }
  510. public List<GroupMembersData> GroupMembersRequest(IClientAPI remoteClient, UUID groupID)
  511. {
  512. if (m_debugEnabled)
  513. m_log.DebugFormat(
  514. "[Groups]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name);
  515. List<GroupMembersData> data = m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), groupID);
  516. if (m_debugEnabled)
  517. {
  518. foreach (GroupMembersData member in data)
  519. {
  520. m_log.DebugFormat("[Groups]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner);
  521. }
  522. }
  523. return data;
  524. }
  525. public List<GroupRolesData> GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID)
  526. {
  527. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  528. List<GroupRolesData> data = m_groupData.GetGroupRoles(GetRequestingAgentIDStr(remoteClient), groupID);
  529. return data;
  530. }
  531. public List<GroupRoleMembersData> GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID)
  532. {
  533. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  534. List<GroupRoleMembersData> data = m_groupData.GetGroupRoleMembers(GetRequestingAgentIDStr(remoteClient), groupID);
  535. if (m_debugEnabled)
  536. {
  537. foreach (GroupRoleMembersData member in data)
  538. {
  539. m_log.DebugFormat("[Groups]: Member({0}) - Role({1})", member.MemberID, member.RoleID);
  540. }
  541. }
  542. return data;
  543. }
  544. public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID)
  545. {
  546. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  547. GroupProfileData profile = new GroupProfileData();
  548. // just to get the OwnerRole...
  549. ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), groupID, string.Empty);
  550. GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
  551. if (groupInfo != null)
  552. {
  553. profile.AllowPublish = groupInfo.AllowPublish;
  554. profile.Charter = groupInfo.Charter;
  555. profile.FounderID = groupInfo.FounderID;
  556. profile.GroupID = groupID;
  557. profile.GroupMembershipCount = groupInfo.MemberCount;
  558. profile.GroupRolesCount = groupInfo.RoleCount;
  559. profile.InsigniaID = groupInfo.GroupPicture;
  560. profile.MaturePublish = groupInfo.MaturePublish;
  561. profile.MembershipFee = groupInfo.MembershipFee;
  562. profile.Money = 0;
  563. profile.Name = groupInfo.GroupName;
  564. profile.OpenEnrollment = groupInfo.OpenEnrollment;
  565. profile.OwnerRole = groupInfo.OwnerRoleID;
  566. profile.ShowInList = groupInfo.ShowInList;
  567. }
  568. if (memberInfo != null)
  569. {
  570. profile.MemberTitle = memberInfo.GroupTitle;
  571. profile.PowersMask = memberInfo.GroupPowers;
  572. }
  573. return profile;
  574. }
  575. public GroupMembershipData[] GetMembershipData(UUID agentID)
  576. {
  577. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  578. return m_groupData.GetAgentGroupMemberships(UUID.Zero.ToString(), agentID.ToString()).ToArray();
  579. }
  580. public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID)
  581. {
  582. if (m_debugEnabled)
  583. m_log.DebugFormat(
  584. "[Groups]: {0} called with groupID={1}, agentID={2}",
  585. System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID);
  586. return m_groupData.GetAgentGroupMembership(UUID.Zero.ToString(), agentID.ToString(), groupID);
  587. }
  588. public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
  589. {
  590. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  591. // Note: Permissions checking for modification rights is handled by the Groups Server/Service
  592. string reason = string.Empty;
  593. if (!m_groupData.UpdateGroup(GetRequestingAgentIDStr(remoteClient), groupID, charter, showInList, insigniaID, membershipFee,
  594. openEnrollment, allowPublish, maturePublish, out reason))
  595. remoteClient.SendAgentAlertMessage(reason, false);
  596. }
  597. public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile)
  598. {
  599. // Note: Permissions checking for modification rights is handled by the Groups Server/Service
  600. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  601. m_groupData.UpdateMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, acceptNotices, listInProfile);
  602. }
  603. public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
  604. {
  605. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called in {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Scene.RegionInfo.RegionName);
  606. if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), UUID.Zero, name) != null)
  607. {
  608. remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists.");
  609. return UUID.Zero;
  610. }
  611. // check user level
  612. ScenePresence avatar = null;
  613. Scene scene = (Scene)remoteClient.Scene;
  614. scene.TryGetScenePresence(remoteClient.AgentId, out avatar);
  615. if (avatar != null)
  616. {
  617. if (avatar.UserLevel < m_levelGroupCreate)
  618. {
  619. remoteClient.SendCreateGroupReply(UUID.Zero, false, String.Format("Insufficient permissions to create a group. Requires level {0}", m_levelGroupCreate));
  620. return UUID.Zero;
  621. }
  622. }
  623. // check funds
  624. // is there is a money module present ?
  625. IMoneyModule money = scene.RequestModuleInterface<IMoneyModule>();
  626. if (money != null)
  627. {
  628. // do the transaction, that is if the agent has got sufficient funds
  629. if (!money.AmountCovered(remoteClient.AgentId, money.GroupCreationCharge)) {
  630. remoteClient.SendCreateGroupReply(UUID.Zero, false, "Insufficient funds to create a group.");
  631. return UUID.Zero;
  632. }
  633. }
  634. string reason = string.Empty;
  635. UUID groupID = m_groupData.CreateGroup(remoteClient.AgentId, name, charter, showInList, insigniaID, membershipFee, openEnrollment,
  636. allowPublish, maturePublish, remoteClient.AgentId, out reason);
  637. if (groupID != UUID.Zero)
  638. {
  639. if (money != null)
  640. money.ApplyCharge(remoteClient.AgentId, money.GroupCreationCharge, MoneyTransactionType.GroupCreate);
  641. remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly");
  642. // Update the founder with new group information.
  643. SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
  644. }
  645. else
  646. remoteClient.SendCreateGroupReply(groupID, false, reason);
  647. return groupID;
  648. }
  649. public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID)
  650. {
  651. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  652. // ToDo: check if agent is a member of group and is allowed to see notices?
  653. List<ExtendedGroupNoticeData> notices = m_groupData.GetGroupNotices(GetRequestingAgentIDStr(remoteClient), groupID);
  654. List<GroupNoticeData> os_notices = new List<GroupNoticeData>();
  655. foreach (ExtendedGroupNoticeData n in notices)
  656. {
  657. GroupNoticeData osn = n.ToGroupNoticeData();
  658. os_notices.Add(osn);
  659. }
  660. return os_notices.ToArray();
  661. }
  662. /// <summary>
  663. /// Get the title of the agent's current role.
  664. /// </summary>
  665. public string GetGroupTitle(UUID avatarID)
  666. {
  667. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  668. GroupMembershipData membership = m_groupData.GetAgentActiveMembership(UUID.Zero.ToString(), avatarID.ToString());
  669. if (membership != null)
  670. {
  671. return membership.GroupTitle;
  672. }
  673. return string.Empty;
  674. }
  675. /// <summary>
  676. /// Change the current Active Group Role for Agent
  677. /// </summary>
  678. public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID)
  679. {
  680. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  681. m_groupData.SetAgentActiveGroupRole(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, titleRoleID);
  682. // TODO: Not sure what all is needed here, but if the active group role change is for the group
  683. // the client currently has set active, then we need to do a scene presence update too
  684. // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID)
  685. UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient));
  686. }
  687. public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType)
  688. {
  689. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  690. // Security Checks are handled in the Groups Service.
  691. switch ((OpenMetaverse.GroupRoleUpdate)updateType)
  692. {
  693. case OpenMetaverse.GroupRoleUpdate.Create:
  694. string reason = string.Empty;
  695. if (!m_groupData.AddGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, UUID.Random(), name, description, title, powers, out reason))
  696. remoteClient.SendAgentAlertMessage("Unable to create role: " + reason, false);
  697. break;
  698. case OpenMetaverse.GroupRoleUpdate.Delete:
  699. m_groupData.RemoveGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID);
  700. break;
  701. case OpenMetaverse.GroupRoleUpdate.UpdateAll:
  702. case OpenMetaverse.GroupRoleUpdate.UpdateData:
  703. case OpenMetaverse.GroupRoleUpdate.UpdatePowers:
  704. if (m_debugEnabled)
  705. {
  706. GroupPowers gp = (GroupPowers)powers;
  707. m_log.DebugFormat("[Groups]: Role ({0}) updated with Powers ({1}) ({2})", name, powers.ToString(), gp.ToString());
  708. }
  709. m_groupData.UpdateGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID, name, description, title, powers);
  710. break;
  711. case OpenMetaverse.GroupRoleUpdate.NoUpdate:
  712. default:
  713. // No Op
  714. break;
  715. }
  716. // TODO: This update really should send out updates for everyone in the role that just got changed.
  717. SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
  718. }
  719. public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes)
  720. {
  721. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  722. // Todo: Security check
  723. switch (changes)
  724. {
  725. case 0:
  726. // Add
  727. m_groupData.AddAgentToGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID);
  728. break;
  729. case 1:
  730. // Remove
  731. m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID);
  732. break;
  733. default:
  734. m_log.ErrorFormat("[Groups]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes);
  735. break;
  736. }
  737. // TODO: This update really should send out updates for everyone in the role that just got changed.
  738. SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
  739. }
  740. public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID)
  741. {
  742. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called for notice {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, groupNoticeID);
  743. GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested);
  744. OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient));
  745. }
  746. public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog)
  747. {
  748. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  749. GridInstantMessage msg = new GridInstantMessage();
  750. byte[] bucket;
  751. msg.imSessionID = groupNoticeID.Guid;
  752. msg.toAgentID = agentID.Guid;
  753. msg.dialog = dialog;
  754. // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice;
  755. msg.fromGroup = true;
  756. msg.offline = (byte)0;
  757. msg.ParentEstateID = 0;
  758. msg.Position = Vector3.Zero;
  759. msg.RegionID = UUID.Zero.Guid;
  760. GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID.ToString(), groupNoticeID);
  761. if (info != null)
  762. {
  763. msg.fromAgentID = info.GroupID.Guid;
  764. msg.timestamp = info.noticeData.Timestamp;
  765. msg.fromAgentName = info.noticeData.FromName;
  766. msg.message = info.noticeData.Subject + "|" + info.Message;
  767. if (info.noticeData.HasAttachment)
  768. {
  769. byte[] name = System.Text.Encoding.UTF8.GetBytes(info.noticeData.AttachmentName);
  770. bucket = new byte[19 + name.Length];
  771. bucket[0] = 1; // has attachment?
  772. bucket[1] = info.noticeData.AttachmentType; // attachment type
  773. name.CopyTo(bucket, 18);
  774. }
  775. else
  776. {
  777. bucket = new byte[19];
  778. bucket[0] = 0; // Has att?
  779. bucket[1] = 0; // type
  780. bucket[18] = 0; // null terminated
  781. }
  782. info.GroupID.ToBytes(bucket, 2);
  783. msg.binaryBucket = bucket;
  784. }
  785. else
  786. {
  787. m_log.DebugFormat("[Groups]: Group Notice {0} not found, composing empty message.", groupNoticeID);
  788. msg.fromAgentID = UUID.Zero.Guid;
  789. msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ;
  790. msg.fromAgentName = string.Empty;
  791. msg.message = string.Empty;
  792. msg.binaryBucket = new byte[0];
  793. }
  794. return msg;
  795. }
  796. public void SendAgentGroupDataUpdate(IClientAPI remoteClient)
  797. {
  798. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  799. // Send agent information about his groups
  800. SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
  801. }
  802. public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID)
  803. {
  804. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  805. string reason = string.Empty;
  806. // Should check to see if OpenEnrollment, or if there's an outstanding invitation
  807. if (m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, UUID.Zero, string.Empty, out reason))
  808. {
  809. remoteClient.SendJoinGroupReply(groupID, true);
  810. // Should this send updates to everyone in the group?
  811. SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
  812. if (reason != string.Empty)
  813. // A warning
  814. remoteClient.SendAlertMessage("Warning: " + reason);
  815. }
  816. else
  817. remoteClient.SendJoinGroupReply(groupID, false);
  818. }
  819. public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID)
  820. {
  821. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  822. m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID);
  823. remoteClient.SendLeaveGroupReply(groupID, true);
  824. remoteClient.SendAgentDropGroup(groupID);
  825. // SL sends out notifcations to the group messaging session that the person has left
  826. // Should this also update everyone who is in the group?
  827. SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient));
  828. }
  829. public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID)
  830. {
  831. EjectGroupMember(remoteClient, GetRequestingAgentID(remoteClient), groupID, ejecteeID);
  832. }
  833. public void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID)
  834. {
  835. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  836. // Todo: Security check?
  837. m_groupData.RemoveAgentFromGroup(agentID.ToString(), ejecteeID.ToString(), groupID);
  838. string agentName;
  839. RegionInfo regionInfo;
  840. // remoteClient provided or just agentID?
  841. if (remoteClient != null)
  842. {
  843. agentName = remoteClient.Name;
  844. regionInfo = remoteClient.Scene.RegionInfo;
  845. remoteClient.SendEjectGroupMemberReply(agentID, groupID, true);
  846. }
  847. else
  848. {
  849. IClientAPI client = GetActiveClient(agentID);
  850. if (client != null)
  851. {
  852. agentName = client.Name;
  853. regionInfo = client.Scene.RegionInfo;
  854. client.SendEjectGroupMemberReply(agentID, groupID, true);
  855. }
  856. else
  857. {
  858. regionInfo = m_sceneList[0].RegionInfo;
  859. UserAccount acc = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID);
  860. if (acc != null)
  861. {
  862. agentName = acc.FirstName + " " + acc.LastName;
  863. }
  864. else
  865. {
  866. agentName = "Unknown member";
  867. }
  868. }
  869. }
  870. GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null);
  871. UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, ejecteeID);
  872. if ((groupInfo == null) || (account == null))
  873. {
  874. return;
  875. }
  876. // Send Message to Ejectee
  877. GridInstantMessage msg = new GridInstantMessage();
  878. msg.imSessionID = UUID.Zero.Guid;
  879. msg.fromAgentID = agentID.Guid;
  880. // msg.fromAgentID = info.GroupID;
  881. msg.toAgentID = ejecteeID.Guid;
  882. //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
  883. msg.timestamp = 0;
  884. msg.fromAgentName = agentName;
  885. msg.message = string.Format("You have been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName);
  886. msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent;
  887. msg.fromGroup = false;
  888. msg.offline = (byte)0;
  889. msg.ParentEstateID = 0;
  890. msg.Position = Vector3.Zero;
  891. msg.RegionID = regionInfo.RegionID.Guid;
  892. msg.binaryBucket = new byte[0];
  893. OutgoingInstantMessage(msg, ejecteeID);
  894. // Message to ejector
  895. // Interop, received special 210 code for ejecting a group member
  896. // this only works within the comms servers domain, and won't work hypergrid
  897. // TODO:FIXME: Use a presense server of some kind to find out where the
  898. // client actually is, and try contacting that region directly to notify them,
  899. // or provide the notification via xmlrpc update queue
  900. msg = new GridInstantMessage();
  901. msg.imSessionID = UUID.Zero.Guid;
  902. msg.fromAgentID = agentID.Guid;
  903. msg.toAgentID = agentID.Guid;
  904. msg.timestamp = 0;
  905. msg.fromAgentName = agentName;
  906. if (account != null)
  907. {
  908. msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, account.FirstName + " " + account.LastName);
  909. }
  910. else
  911. {
  912. msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, "Unknown member");
  913. }
  914. msg.dialog = (byte)210; //interop
  915. msg.fromGroup = false;
  916. msg.offline = (byte)0;
  917. msg.ParentEstateID = 0;
  918. msg.Position = Vector3.Zero;
  919. msg.RegionID = regionInfo.RegionID.Guid;
  920. msg.binaryBucket = new byte[0];
  921. OutgoingInstantMessage(msg, agentID);
  922. // SL sends out messages to everyone in the group
  923. // Who all should receive updates and what should they be updated with?
  924. UpdateAllClientsWithGroupInfo(ejecteeID);
  925. }
  926. public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID)
  927. {
  928. InviteGroup(remoteClient, GetRequestingAgentID(remoteClient), groupID, invitedAgentID, roleID);
  929. }
  930. public void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID)
  931. {
  932. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  933. string agentName = m_UserManagement.GetUserName(agentID);
  934. RegionInfo regionInfo = m_sceneList[0].RegionInfo;
  935. GroupRecord group = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null);
  936. if (group == null)
  937. {
  938. m_log.DebugFormat("[Groups]: No such group {0}", groupID);
  939. return;
  940. }
  941. // Todo: Security check, probably also want to send some kind of notification
  942. UUID InviteID = UUID.Random();
  943. if (m_groupData.AddAgentToGroupInvite(agentID.ToString(), InviteID, groupID, roleID, invitedAgentID.ToString()))
  944. {
  945. if (m_msgTransferModule != null)
  946. {
  947. Guid inviteUUID = InviteID.Guid;
  948. GridInstantMessage msg = new GridInstantMessage();
  949. msg.imSessionID = inviteUUID;
  950. // msg.fromAgentID = agentID.Guid;
  951. msg.fromAgentID = groupID.Guid;
  952. msg.toAgentID = invitedAgentID.Guid;
  953. //msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
  954. msg.timestamp = 0;
  955. msg.fromAgentName = agentName;
  956. msg.message = string.Format("{0} has invited you to join a group called {1}. There is no cost to join this group.", agentName, group.GroupName);
  957. msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
  958. msg.fromGroup = true;
  959. msg.offline = (byte)0;
  960. msg.ParentEstateID = 0;
  961. msg.Position = Vector3.Zero;
  962. msg.RegionID = regionInfo.RegionID.Guid;
  963. msg.binaryBucket = new byte[20];
  964. OutgoingInstantMessage(msg, invitedAgentID);
  965. }
  966. }
  967. }
  968. public List<DirGroupsReplyData> FindGroups(IClientAPI remoteClient, string query)
  969. {
  970. return m_groupData.FindGroups(GetRequestingAgentIDStr(remoteClient), query);
  971. }
  972. #endregion
  973. #region Client/Update Tools
  974. /// <summary>
  975. /// Try to find an active IClientAPI reference for agentID giving preference to root connections
  976. /// </summary>
  977. private IClientAPI GetActiveClient(UUID agentID)
  978. {
  979. IClientAPI child = null;
  980. // Try root avatar first
  981. foreach (Scene scene in m_sceneList)
  982. {
  983. ScenePresence sp = scene.GetScenePresence(agentID);
  984. if (sp != null)
  985. {
  986. if (!sp.IsChildAgent)
  987. {
  988. return sp.ControllingClient;
  989. }
  990. else
  991. {
  992. child = sp.ControllingClient;
  993. }
  994. }
  995. }
  996. // If we didn't find a root, then just return whichever child we found, or null if none
  997. return child;
  998. }
  999. /// <summary>
  1000. /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'.
  1001. /// </summary>
  1002. private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data)
  1003. {
  1004. if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  1005. // NPCs currently don't have a CAPs structure or event queues. There is a strong argument for conveying this information
  1006. // to them anyway since it makes writing server-side bots a lot easier, but for now we don't do anything.
  1007. if (remoteClient.SceneAgent.PresenceType == PresenceType.Npc)
  1008. return;
  1009. OSDArray AgentData = new OSDArray(1);
  1010. OSDMap AgentDataMap = new OSDMap(1);
  1011. AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID));
  1012. AgentData.Add(AgentDataMap);
  1013. OSDArray GroupData = new OSDArray(data.Length);
  1014. OSDArray NewGroupData = new OSDArray(data.Length);
  1015. foreach (GroupMembershipData membership in data)
  1016. {
  1017. if (GetRequestingAgentID(remoteClient) != dataForAgentID)
  1018. {
  1019. if (!membership.ListInProfile)
  1020. {
  1021. // If we're sending group info to remoteclient about another agent,
  1022. // filter out groups the other agent doesn't want to share.
  1023. continue;
  1024. }
  1025. }
  1026. OSDMap GroupDataMap = new OSDMap(6);
  1027. OSDMap NewGroupDataMap = new OSDMap(1);
  1028. GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID));
  1029. GroupDataMap.Add("GroupPowers", OSD.FromULong(membership.GroupPowers));
  1030. GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices));
  1031. GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture));
  1032. GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution));
  1033. GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName));
  1034. NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile));
  1035. GroupData.Add(GroupDataMap);
  1036. NewGroupData.Add(NewGroupDataMap);
  1037. }
  1038. OSDMap llDataStruct = new OSDMap(3);
  1039. llDataStruct.Add("AgentData", AgentData);
  1040. llDataStruct.Add("GroupData", GroupData);
  1041. llDataStruct.Add("NewGroupData", NewGroupData);
  1042. if (m_debugEnabled)
  1043. {
  1044. m_log.InfoFormat("[Groups]: {0}", OSDParser.SerializeJsonString(llDataStruct));
  1045. }
  1046. IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
  1047. if (queue != null)
  1048. {
  1049. queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient));
  1050. }
  1051. }
  1052. private void SendScenePresenceUpdate(UUID AgentID, string Title)
  1053. {
  1054. if (m_debugEnabled) m_log.DebugFormat("[Groups]: Updating scene title for {0} with title: {1}", AgentID, Title);
  1055. ScenePresence presence = null;
  1056. foreach (Scene scene in m_sceneList)
  1057. {
  1058. presence = scene.GetScenePresence(AgentID);
  1059. if (presence != null)
  1060. {
  1061. if (presence.Grouptitle != Title)
  1062. {
  1063. presence.Grouptitle = Title;
  1064. if (! presence.IsChildAgent)
  1065. presence.SendAvatarDataToAllAgents();
  1066. }
  1067. }
  1068. }
  1069. }
  1070. /// <summary>
  1071. /// Send updates to all clients who might be interested in groups data for dataForClientID
  1072. /// </summary>
  1073. private void UpdateAllClientsWithGroupInfo(UUID dataForClientID)
  1074. {
  1075. if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  1076. // TODO: Probably isn't nessesary to update every client in every scene.
  1077. // Need to examine client updates and do only what's nessesary.
  1078. lock (m_sceneList)
  1079. {
  1080. foreach (Scene scene in m_sceneList)
  1081. {
  1082. scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); });
  1083. }
  1084. }
  1085. }
  1086. /// <summary>
  1087. /// Update remoteClient with group information about dataForAgentID
  1088. /// </summary>
  1089. private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID)
  1090. {
  1091. if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name);
  1092. // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
  1093. OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero);
  1094. // Need to send a group membership update to the client
  1095. // UDP version doesn't seem to behave nicely. But we're going to send it out here
  1096. // with an empty group membership to hopefully remove groups being displayed due
  1097. // to the core Groups Stub
  1098. //remoteClient.SendGroupMembership(new GroupMembershipData[0]);
  1099. GroupMembershipData[] membershipArray = GetProfileListedGroupMemberships(remoteClient, dataForAgentID);
  1100. SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipArray);
  1101. //remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipArray);
  1102. if (remoteClient.AgentId == dataForAgentID)
  1103. remoteClient.RefreshGroupMembership();
  1104. }
  1105. /// <summary>
  1106. /// Get a list of groups memberships for the agent that are marked "ListInProfile"
  1107. /// (unless that agent has a godLike aspect, in which case get all groups)
  1108. /// </summary>
  1109. /// <param name="dataForAgentID"></param>
  1110. /// <returns></returns>
  1111. private GroupMembershipData[] GetProfileListedGroupMemberships(IClientAPI requestingClient, UUID dataForAgentID)
  1112. {
  1113. List<GroupMembershipData> membershipData = m_groupData.GetAgentGroupMemberships(requestingClient.AgentId.ToString(), dataForAgentID.ToString());
  1114. GroupMembershipData[] membershipArray;
  1115. // cScene and property accessor 'isGod' are in support of the opertions to bypass 'hidden' group attributes for
  1116. // those with a GodLike aspect.
  1117. Scene cScene = (Scene)requestingClient.Scene;
  1118. bool isGod = cScene.Permissions.IsGod(requestingClient.AgentId);
  1119. if (isGod)
  1120. {
  1121. membershipArray = membershipData.ToArray();
  1122. }
  1123. else
  1124. {
  1125. if (requestingClient.AgentId != dataForAgentID)
  1126. {
  1127. Predicate<GroupMembershipData> showInProfile = delegate(GroupMembershipData membership)
  1128. {
  1129. return membership.ListInProfile;
  1130. };
  1131. membershipArray = membershipData.FindAll(showInProfile).ToArray();
  1132. }
  1133. else
  1134. {
  1135. membershipArray = membershipData.ToArray();
  1136. }
  1137. }
  1138. if (m_debugEnabled)
  1139. {
  1140. m_log.InfoFormat("[Groups]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId);
  1141. foreach (GroupMembershipData membership in membershipArray)
  1142. {
  1143. m_log.InfoFormat("[Groups]: {0} :: {1} - {2} - {3}", dataForAgentID, membership.GroupName, membership.GroupTitle, membership.GroupPowers);
  1144. }
  1145. }
  1146. return membershipArray;
  1147. }
  1148. private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle)
  1149. {
  1150. if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  1151. // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff
  1152. string firstname = "Unknown", lastname = "Unknown";
  1153. string name = m_UserManagement.GetUserName(dataForAgentID);
  1154. if (!string.IsNullOrEmpty(name))
  1155. {
  1156. string[] parts = name.Split(new char[] { ' ' });
  1157. if (parts.Length >= 2)
  1158. {
  1159. firstname = parts[0];
  1160. lastname = parts[1];
  1161. }
  1162. }
  1163. remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname,
  1164. lastname, activeGroupPowers, activeGroupName,
  1165. activeGroupTitle);
  1166. }
  1167. #endregion
  1168. #region IM Backed Processes
  1169. private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo)
  1170. {
  1171. if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
  1172. IClientAPI localClient = GetActiveClient(msgTo);
  1173. if (localClient != null)
  1174. {
  1175. if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is local, delivering directly", localClient.Name);
  1176. localClient.SendInstantMessage(msg);
  1177. }
  1178. else if (m_msgTransferModule != null)
  1179. {
  1180. if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo);
  1181. m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[Groups]: Message Sent: {0}", success?"Succeeded":"Failed"); });
  1182. }
  1183. }
  1184. public void NotifyChange(UUID groupID)
  1185. {
  1186. // Notify all group members of a chnge in group roles and/or
  1187. // permissions
  1188. //
  1189. }
  1190. #endregion
  1191. private string GetRequestingAgentIDStr(IClientAPI client)
  1192. {
  1193. return GetRequestingAgentID(client).ToString();
  1194. }
  1195. private UUID GetRequestingAgentID(IClientAPI client)
  1196. {
  1197. UUID requestingAgentID = UUID.Zero;
  1198. if (client != null)
  1199. {
  1200. requestingAgentID = client.AgentId;
  1201. }
  1202. return requestingAgentID;
  1203. }
  1204. }
  1205. }