GroupsModule.cs 66 KB

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