GroupsModule.cs 66 KB

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