GroupsModule.cs 63 KB

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