FriendsModule.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  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;
  29. using System.Collections.Generic;
  30. using System.Linq;
  31. using System.Reflection;
  32. using System.Threading;
  33. using log4net;
  34. using Nini.Config;
  35. using Nwc.XmlRpc;
  36. using OpenMetaverse;
  37. using Mono.Addins;
  38. using OpenSim.Framework;
  39. using OpenSim.Framework.Servers.HttpServer;
  40. using OpenSim.Framework.Communications;
  41. using OpenSim.Framework.Servers;
  42. using OpenSim.Region.Framework.Interfaces;
  43. using OpenSim.Region.Framework.Scenes;
  44. using OpenSim.Services.Interfaces;
  45. using OpenSim.Services.Connectors.Friends;
  46. using OpenSim.Server.Base;
  47. using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
  48. using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
  49. using GridRegion = OpenSim.Services.Interfaces.GridRegion;
  50. namespace OpenSim.Region.CoreModules.Avatar.Friends
  51. {
  52. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FriendsModule")]
  53. public class FriendsModule : ISharedRegionModule, IFriendsModule
  54. {
  55. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  56. protected bool m_Enabled = false;
  57. protected class UserFriendData
  58. {
  59. public UUID PrincipalID;
  60. public FriendInfo[] Friends;
  61. public int Refcount;
  62. public bool IsFriend(string friend)
  63. {
  64. foreach (FriendInfo fi in Friends)
  65. {
  66. if (fi.Friend == friend)
  67. return true;
  68. }
  69. return false;
  70. }
  71. }
  72. protected static readonly FriendInfo[] EMPTY_FRIENDS = new FriendInfo[0];
  73. protected List<Scene> m_Scenes = new List<Scene>();
  74. protected IPresenceService m_PresenceService = null;
  75. protected IFriendsService m_FriendsService = null;
  76. protected FriendsSimConnector m_FriendsSimConnector;
  77. /// <summary>
  78. /// Cache friends lists for users.
  79. /// </summary>
  80. /// <remarks>
  81. /// This is a complex and error-prone thing to do. At the moment, we assume that the efficiency gained in
  82. /// permissions checks outweighs the disadvantages of that complexity.
  83. /// </remarks>
  84. protected Dictionary<UUID, UserFriendData> m_Friends = new Dictionary<UUID, UserFriendData>();
  85. /// <summary>
  86. /// Maintain a record of viewers that need to be sent notifications for friends that are online. This only
  87. /// needs to be done on login. Subsequent online/offline friend changes are sent by a different mechanism.
  88. /// </summary>
  89. protected HashSet<UUID> m_NeedsListOfOnlineFriends = new HashSet<UUID>();
  90. protected IPresenceService PresenceService
  91. {
  92. get
  93. {
  94. if (m_PresenceService == null)
  95. {
  96. if (m_Scenes.Count > 0)
  97. m_PresenceService = m_Scenes[0].RequestModuleInterface<IPresenceService>();
  98. }
  99. return m_PresenceService;
  100. }
  101. }
  102. public IFriendsService FriendsService
  103. {
  104. get
  105. {
  106. if (m_FriendsService == null)
  107. {
  108. if (m_Scenes.Count > 0)
  109. m_FriendsService = m_Scenes[0].RequestModuleInterface<IFriendsService>();
  110. }
  111. return m_FriendsService;
  112. }
  113. }
  114. protected IGridService GridService
  115. {
  116. get { return m_Scenes[0].GridService; }
  117. }
  118. public IUserAccountService UserAccountService
  119. {
  120. get { return m_Scenes[0].UserAccountService; }
  121. }
  122. public IScene Scene
  123. {
  124. get
  125. {
  126. if (m_Scenes.Count > 0)
  127. return m_Scenes[0];
  128. else
  129. return null;
  130. }
  131. }
  132. #region ISharedRegionModule
  133. public void Initialise(IConfigSource config)
  134. {
  135. IConfig moduleConfig = config.Configs["Modules"];
  136. if (moduleConfig != null)
  137. {
  138. string name = moduleConfig.GetString("FriendsModule", "FriendsModule");
  139. if (name == Name)
  140. {
  141. InitModule(config);
  142. m_Enabled = true;
  143. m_log.DebugFormat("[FRIENDS MODULE]: {0} enabled.", Name);
  144. }
  145. }
  146. }
  147. protected virtual void InitModule(IConfigSource config)
  148. {
  149. IConfig friendsConfig = config.Configs["Friends"];
  150. if (friendsConfig != null)
  151. {
  152. int mPort = friendsConfig.GetInt("Port", 0);
  153. string connector = friendsConfig.GetString("Connector", String.Empty);
  154. Object[] args = new Object[] { config };
  155. m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(connector, args);
  156. m_FriendsSimConnector = new FriendsSimConnector();
  157. // Instantiate the request handler
  158. IHttpServer server = MainServer.GetHttpServer((uint)mPort);
  159. if (server != null)
  160. server.AddStreamHandler(new FriendsRequestHandler(this));
  161. }
  162. if (m_FriendsService == null)
  163. {
  164. m_log.Error("[FRIENDS]: No Connector defined in section Friends, or failed to load, cannot continue");
  165. throw new Exception("Connector load error");
  166. }
  167. }
  168. public void PostInitialise()
  169. {
  170. }
  171. public void Close()
  172. {
  173. }
  174. public virtual void AddRegion(Scene scene)
  175. {
  176. if (!m_Enabled)
  177. return;
  178. // m_log.DebugFormat("[FRIENDS MODULE]: AddRegion on {0}", Name);
  179. m_Scenes.Add(scene);
  180. scene.RegisterModuleInterface<IFriendsModule>(this);
  181. scene.EventManager.OnNewClient += OnNewClient;
  182. scene.EventManager.OnClientClosed += OnClientClosed;
  183. scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
  184. scene.EventManager.OnClientLogin += OnClientLogin;
  185. }
  186. public virtual void RegionLoaded(Scene scene) {}
  187. public void RemoveRegion(Scene scene)
  188. {
  189. if (!m_Enabled)
  190. return;
  191. m_Scenes.Remove(scene);
  192. }
  193. public virtual string Name
  194. {
  195. get { return "FriendsModule"; }
  196. }
  197. public Type ReplaceableInterface
  198. {
  199. get { return null; }
  200. }
  201. #endregion
  202. public virtual int GetRightsGrantedByFriend(UUID principalID, UUID friendID)
  203. {
  204. FriendInfo[] friends = GetFriendsFromCache(principalID);
  205. FriendInfo finfo = GetFriend(friends, friendID);
  206. if (finfo != null)
  207. {
  208. return finfo.TheirFlags;
  209. }
  210. return 0;
  211. }
  212. private void OnNewClient(IClientAPI client)
  213. {
  214. client.OnInstantMessage += OnInstantMessage;
  215. client.OnApproveFriendRequest += OnApproveFriendRequest;
  216. client.OnDenyFriendRequest += OnDenyFriendRequest;
  217. client.OnTerminateFriendship += RemoveFriendship;
  218. client.OnGrantUserRights += GrantRights;
  219. // We need to cache information for child agents as well as root agents so that friend edit/move/delete
  220. // permissions will work across borders where both regions are on different simulators.
  221. //
  222. // Do not do this asynchronously. If we do, then subsequent code can outrace CacheFriends() and
  223. // return misleading results from the still empty friends cache.
  224. // If we absolutely need to do this asynchronously, then a signalling mechanism is needed so that calls
  225. // to GetFriends() will wait until CacheFriends() completes. Locks are insufficient.
  226. CacheFriends(client);
  227. }
  228. /// <summary>
  229. /// Cache the friends list or increment the refcount for the existing friends list.
  230. /// </summary>
  231. /// <param name="client">
  232. /// </param>
  233. /// <returns>
  234. /// Returns true if the list was fetched, false if it wasn't
  235. /// </returns>
  236. protected virtual bool CacheFriends(IClientAPI client)
  237. {
  238. UUID agentID = client.AgentId;
  239. lock (m_Friends)
  240. {
  241. UserFriendData friendsData;
  242. if (m_Friends.TryGetValue(agentID, out friendsData))
  243. {
  244. friendsData.Refcount++;
  245. return false;
  246. }
  247. else
  248. {
  249. friendsData = new UserFriendData();
  250. friendsData.PrincipalID = agentID;
  251. friendsData.Friends = GetFriendsFromService(client);
  252. friendsData.Refcount = 1;
  253. m_Friends[agentID] = friendsData;
  254. return true;
  255. }
  256. }
  257. }
  258. private void OnClientClosed(UUID agentID, Scene scene)
  259. {
  260. ScenePresence sp = scene.GetScenePresence(agentID);
  261. if (sp != null && !sp.IsChildAgent)
  262. {
  263. // do this for root agents closing out
  264. StatusChange(agentID, false);
  265. }
  266. lock (m_Friends)
  267. {
  268. UserFriendData friendsData;
  269. if (m_Friends.TryGetValue(agentID, out friendsData))
  270. {
  271. friendsData.Refcount--;
  272. if (friendsData.Refcount <= 0)
  273. m_Friends.Remove(agentID);
  274. }
  275. }
  276. }
  277. private void OnMakeRootAgent(ScenePresence sp)
  278. {
  279. RecacheFriends(sp.ControllingClient);
  280. }
  281. private void OnClientLogin(IClientAPI client)
  282. {
  283. UUID agentID = client.AgentId;
  284. //m_log.DebugFormat("[XXX]: OnClientLogin!");
  285. // Inform the friends that this user is online
  286. StatusChange(agentID, true);
  287. // Register that we need to send the list of online friends to this user
  288. lock (m_NeedsListOfOnlineFriends)
  289. m_NeedsListOfOnlineFriends.Add(agentID);
  290. }
  291. public virtual bool SendFriendsOnlineIfNeeded(IClientAPI client)
  292. {
  293. UUID agentID = client.AgentId;
  294. // Check if the online friends list is needed
  295. lock (m_NeedsListOfOnlineFriends)
  296. {
  297. if (!m_NeedsListOfOnlineFriends.Remove(agentID))
  298. return false;
  299. }
  300. // Send the friends online
  301. List<UUID> online = GetOnlineFriends(agentID);
  302. if (online.Count > 0)
  303. client.SendAgentOnline(online.ToArray());
  304. // Send outstanding friendship offers
  305. List<string> outstanding = new List<string>();
  306. FriendInfo[] friends = GetFriendsFromCache(agentID);
  307. foreach (FriendInfo fi in friends)
  308. {
  309. if (fi.TheirFlags == -1)
  310. outstanding.Add(fi.Friend);
  311. }
  312. GridInstantMessage im = new GridInstantMessage(client.Scene, UUID.Zero, String.Empty, agentID, (byte)InstantMessageDialog.FriendshipOffered,
  313. "Will you be my friend?", true, Vector3.Zero);
  314. foreach (string fid in outstanding)
  315. {
  316. UUID fromAgentID;
  317. string firstname = "Unknown", lastname = "UserFMSFOIN";
  318. if (!GetAgentInfo(client.Scene.RegionInfo.ScopeID, fid, out fromAgentID, out firstname, out lastname))
  319. {
  320. m_log.DebugFormat("[FRIENDS MODULE]: skipping malformed friend {0}", fid);
  321. continue;
  322. }
  323. im.offline = 0;
  324. im.fromAgentID = fromAgentID.Guid;
  325. im.fromAgentName = firstname + " " + lastname;
  326. im.imSessionID = im.fromAgentID;
  327. im.message = FriendshipMessage(fid);
  328. LocalFriendshipOffered(agentID, im);
  329. }
  330. return true;
  331. }
  332. protected virtual string FriendshipMessage(string friendID)
  333. {
  334. return "Will you be my friend?";
  335. }
  336. protected virtual bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last)
  337. {
  338. first = "Unknown"; last = "UserFMGAI";
  339. if (!UUID.TryParse(fid, out agentID))
  340. return false;
  341. UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(scopeID, agentID);
  342. if (account != null)
  343. {
  344. first = account.FirstName;
  345. last = account.LastName;
  346. }
  347. return true;
  348. }
  349. List<UUID> GetOnlineFriends(UUID userID)
  350. {
  351. List<string> friendList = new List<string>();
  352. FriendInfo[] friends = GetFriendsFromCache(userID);
  353. foreach (FriendInfo fi in friends)
  354. {
  355. if (((fi.TheirFlags & (int)FriendRights.CanSeeOnline) != 0) && (fi.TheirFlags != -1))
  356. friendList.Add(fi.Friend);
  357. }
  358. List<UUID> online = new List<UUID>();
  359. if (friendList.Count > 0)
  360. GetOnlineFriends(userID, friendList, online);
  361. // m_log.DebugFormat(
  362. // "[FRIENDS MODULE]: User {0} has {1} friends online", userID, online.Count);
  363. return online;
  364. }
  365. protected virtual void GetOnlineFriends(UUID userID, List<string> friendList, /*collector*/ List<UUID> online)
  366. {
  367. // m_log.DebugFormat(
  368. // "[FRIENDS MODULE]: Looking for online presence of {0} users for {1}", friendList.Count, userID);
  369. PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray());
  370. foreach (PresenceInfo pi in presence)
  371. {
  372. UUID presenceID;
  373. if (UUID.TryParse(pi.UserID, out presenceID))
  374. online.Add(presenceID);
  375. }
  376. }
  377. /// <summary>
  378. /// Find the client for a ID
  379. /// </summary>
  380. public IClientAPI LocateClientObject(UUID agentID)
  381. {
  382. lock (m_Scenes)
  383. {
  384. foreach (Scene scene in m_Scenes)
  385. {
  386. ScenePresence presence = scene.GetScenePresence(agentID);
  387. if (presence != null && !presence.IsChildAgent)
  388. return presence.ControllingClient;
  389. }
  390. }
  391. return null;
  392. }
  393. /// <summary>
  394. /// Caller beware! Call this only for root agents.
  395. /// </summary>
  396. /// <param name="agentID"></param>
  397. /// <param name="online"></param>
  398. private void StatusChange(UUID agentID, bool online)
  399. {
  400. FriendInfo[] friends = GetFriendsFromCache(agentID);
  401. if (friends.Length > 0)
  402. {
  403. List<FriendInfo> friendList = new List<FriendInfo>();
  404. foreach (FriendInfo fi in friends)
  405. {
  406. if (((fi.MyFlags & (int)FriendRights.CanSeeOnline) != 0) && (fi.TheirFlags != -1))
  407. friendList.Add(fi);
  408. }
  409. Util.FireAndForget(
  410. delegate
  411. {
  412. // m_log.DebugFormat(
  413. // "[FRIENDS MODULE]: Notifying {0} friends of {1} of online status {2}",
  414. // friendList.Count, agentID, online);
  415. // Notify about this user status
  416. StatusNotify(friendList, agentID, online);
  417. }
  418. );
  419. }
  420. }
  421. protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online)
  422. {
  423. //m_log.DebugFormat("[FRIENDS]: Entering StatusNotify for {0}", userID);
  424. List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend);
  425. List<string> remoteFriendStringIds = new List<string>();
  426. foreach (string friendStringId in friendStringIds)
  427. {
  428. UUID friendUuid;
  429. if (UUID.TryParse(friendStringId, out friendUuid))
  430. {
  431. if (LocalStatusNotification(userID, friendUuid, online))
  432. continue;
  433. remoteFriendStringIds.Add(friendStringId);
  434. }
  435. else
  436. {
  437. m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friendStringId);
  438. }
  439. }
  440. // We do this regrouping so that we can efficiently send a single request rather than one for each
  441. // friend in what may be a very large friends list.
  442. PresenceInfo[] friendSessions = PresenceService.GetAgents(remoteFriendStringIds.ToArray());
  443. foreach (PresenceInfo friendSession in friendSessions)
  444. {
  445. // let's guard against sessions-gone-bad
  446. if (friendSession != null && friendSession.RegionID != UUID.Zero)
  447. {
  448. //m_log.DebugFormat("[FRIENDS]: Get region {0}", friendSession.RegionID);
  449. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  450. if (region != null)
  451. {
  452. m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online);
  453. }
  454. }
  455. //else
  456. // m_log.DebugFormat("[FRIENDS]: friend session is null or the region is UUID.Zero");
  457. }
  458. }
  459. protected virtual void OnInstantMessage(IClientAPI client, GridInstantMessage im)
  460. {
  461. if ((InstantMessageDialog)im.dialog == InstantMessageDialog.FriendshipOffered)
  462. {
  463. // we got a friendship offer
  464. UUID principalID = new UUID(im.fromAgentID);
  465. UUID friendID = new UUID(im.toAgentID);
  466. m_log.DebugFormat("[FRIENDS]: {0} ({1}) offered friendship to {2} ({3})", principalID, client.FirstName + client.LastName, friendID, im.fromAgentName);
  467. // Check that the friendship doesn't exist yet
  468. FriendInfo[] finfos = GetFriendsFromCache(principalID);
  469. if (finfos != null)
  470. {
  471. FriendInfo f = GetFriend(finfos, friendID);
  472. if (f != null)
  473. {
  474. client.SendAgentAlertMessage("This person is already your friend. Please delete it first if you want to reestablish the friendship.", false);
  475. return;
  476. }
  477. }
  478. // This user wants to be friends with the other user.
  479. // Let's add the relation backwards, in case the other is not online
  480. StoreBackwards(friendID, principalID);
  481. // Now let's ask the other user to be friends with this user
  482. ForwardFriendshipOffer(principalID, friendID, im);
  483. }
  484. }
  485. protected virtual bool ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im)
  486. {
  487. // !!!!!!!! This is a hack so that we don't have to keep state (transactionID/imSessionID)
  488. // We stick this agent's ID as imSession, so that it's directly available on the receiving end
  489. im.imSessionID = im.fromAgentID;
  490. im.fromAgentName = GetFriendshipRequesterName(agentID);
  491. // Try the local sim
  492. if (LocalFriendshipOffered(friendID, im))
  493. return true;
  494. // The prospective friend is not here [as root]. Let's forward.
  495. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  496. if (friendSessions != null && friendSessions.Length > 0)
  497. {
  498. PresenceInfo friendSession = friendSessions[0];
  499. if (friendSession != null)
  500. {
  501. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  502. m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message);
  503. return true;
  504. }
  505. }
  506. // If the prospective friend is not online, he'll get the message upon login.
  507. return false;
  508. }
  509. protected virtual string GetFriendshipRequesterName(UUID agentID)
  510. {
  511. UserAccount account = UserAccountService.GetUserAccount(UUID.Zero, agentID);
  512. return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName;
  513. }
  514. protected virtual void OnApproveFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  515. {
  516. m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", client.AgentId, friendID);
  517. AddFriendship(client, friendID);
  518. }
  519. public void AddFriendship(IClientAPI client, UUID friendID)
  520. {
  521. StoreFriendships(client.AgentId, friendID);
  522. ICallingCardModule ccm = client.Scene.RequestModuleInterface<ICallingCardModule>();
  523. if (ccm != null)
  524. {
  525. ccm.CreateCallingCard(client.AgentId, friendID, UUID.Zero);
  526. }
  527. // Update the local cache.
  528. RecacheFriends(client);
  529. //
  530. // Notify the friend
  531. //
  532. // Try Local
  533. if (LocalFriendshipApproved(client.AgentId, client.Name, friendID))
  534. {
  535. client.SendAgentOnline(new UUID[] { friendID });
  536. return;
  537. }
  538. // The friend is not here
  539. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  540. if (friendSessions != null && friendSessions.Length > 0)
  541. {
  542. PresenceInfo friendSession = friendSessions[0];
  543. if (friendSession != null)
  544. {
  545. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  546. m_FriendsSimConnector.FriendshipApproved(region, client.AgentId, client.Name, friendID);
  547. client.SendAgentOnline(new UUID[] { friendID });
  548. }
  549. }
  550. }
  551. private void OnDenyFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  552. {
  553. m_log.DebugFormat("[FRIENDS]: {0} denied friendship to {1}", client.AgentId, friendID);
  554. DeleteFriendship(client.AgentId, friendID);
  555. //
  556. // Notify the friend
  557. //
  558. // Try local
  559. if (LocalFriendshipDenied(client.AgentId, client.Name, friendID))
  560. return;
  561. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  562. if (friendSessions != null && friendSessions.Length > 0)
  563. {
  564. PresenceInfo friendSession = friendSessions[0];
  565. if (friendSession != null)
  566. {
  567. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  568. if (region != null)
  569. m_FriendsSimConnector.FriendshipDenied(region, client.AgentId, client.Name, friendID);
  570. else
  571. m_log.WarnFormat("[FRIENDS]: Could not find region {0} in locating {1}", friendSession.RegionID, friendID);
  572. }
  573. }
  574. }
  575. public void RemoveFriendship(IClientAPI client, UUID exfriendID)
  576. {
  577. if (!DeleteFriendship(client.AgentId, exfriendID))
  578. client.SendAlertMessage("Unable to terminate friendship on this sim.");
  579. // Update local cache
  580. RecacheFriends(client);
  581. client.SendTerminateFriend(exfriendID);
  582. //
  583. // Notify the friend
  584. //
  585. // Try local
  586. if (LocalFriendshipTerminated(client.AgentId, exfriendID))
  587. return;
  588. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() });
  589. if (friendSessions != null && friendSessions.Length > 0)
  590. {
  591. PresenceInfo friendSession = friendSessions[0];
  592. if (friendSession != null)
  593. {
  594. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  595. m_FriendsSimConnector.FriendshipTerminated(region, client.AgentId, exfriendID);
  596. }
  597. }
  598. }
  599. public void GrantRights(IClientAPI remoteClient, UUID friendID, int rights)
  600. {
  601. UUID requester = remoteClient.AgentId;
  602. m_log.DebugFormat(
  603. "[FRIENDS MODULE]: User {0} changing rights to {1} for friend {2}",
  604. requester, rights, friendID);
  605. FriendInfo[] friends = GetFriendsFromCache(requester);
  606. if (friends.Length == 0)
  607. {
  608. return;
  609. }
  610. // Let's find the friend in this user's friend list
  611. FriendInfo friend = GetFriend(friends, friendID);
  612. if (friend != null) // Found it
  613. {
  614. // Store it on the DB
  615. if (!StoreRights(requester, friendID, rights))
  616. {
  617. remoteClient.SendAlertMessage("Unable to grant rights.");
  618. return;
  619. }
  620. // Store it in the local cache
  621. int myFlags = friend.MyFlags;
  622. friend.MyFlags = rights;
  623. // Always send this back to the original client
  624. remoteClient.SendChangeUserRights(requester, friendID, rights);
  625. //
  626. // Notify the friend
  627. //
  628. // Try local
  629. if (LocalGrantRights(requester, friendID, myFlags, rights))
  630. return;
  631. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  632. if (friendSessions != null && friendSessions.Length > 0)
  633. {
  634. PresenceInfo friendSession = friendSessions[0];
  635. if (friendSession != null)
  636. {
  637. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  638. // TODO: You might want to send the delta to save the lookup
  639. // on the other end!!
  640. m_FriendsSimConnector.GrantRights(region, requester, friendID, myFlags, rights);
  641. }
  642. }
  643. }
  644. else
  645. {
  646. m_log.DebugFormat("[FRIENDS MODULE]: friend {0} not found for {1}", friendID, requester);
  647. }
  648. }
  649. protected virtual FriendInfo GetFriend(FriendInfo[] friends, UUID friendID)
  650. {
  651. foreach (FriendInfo fi in friends)
  652. {
  653. if (fi.Friend == friendID.ToString())
  654. return fi;
  655. }
  656. return null;
  657. }
  658. #region Local
  659. public virtual bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
  660. {
  661. IClientAPI friendClient = LocateClientObject(toID);
  662. if (friendClient != null)
  663. {
  664. // the prospective friend in this sim as root agent
  665. friendClient.SendInstantMessage(im);
  666. // we're done
  667. return true;
  668. }
  669. return false;
  670. }
  671. public bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID)
  672. {
  673. IClientAPI friendClient = LocateClientObject(friendID);
  674. if (friendClient != null)
  675. {
  676. // the prospective friend in this sim as root agent
  677. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  678. (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, userID.ToString(), false, Vector3.Zero);
  679. friendClient.SendInstantMessage(im);
  680. ICallingCardModule ccm = friendClient.Scene.RequestModuleInterface<ICallingCardModule>();
  681. if (ccm != null)
  682. {
  683. ccm.CreateCallingCard(friendID, userID, UUID.Zero);
  684. }
  685. // Update the local cache
  686. RecacheFriends(friendClient);
  687. // we're done
  688. return true;
  689. }
  690. return false;
  691. }
  692. public bool LocalFriendshipDenied(UUID userID, string userName, UUID friendID)
  693. {
  694. IClientAPI friendClient = LocateClientObject(friendID);
  695. if (friendClient != null)
  696. {
  697. // the prospective friend in this sim as root agent
  698. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  699. (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, userID.ToString(), false, Vector3.Zero);
  700. friendClient.SendInstantMessage(im);
  701. // we're done
  702. return true;
  703. }
  704. return false;
  705. }
  706. public bool LocalFriendshipTerminated(UUID userID, UUID exfriendID)
  707. {
  708. IClientAPI friendClient = LocateClientObject(exfriendID);
  709. if (friendClient != null)
  710. {
  711. // the friend in this sim as root agent
  712. friendClient.SendTerminateFriend(userID);
  713. // update local cache
  714. RecacheFriends(friendClient);
  715. // we're done
  716. return true;
  717. }
  718. return false;
  719. }
  720. public bool LocalGrantRights(UUID userID, UUID friendID, int userFlags, int rights)
  721. {
  722. IClientAPI friendClient = LocateClientObject(friendID);
  723. if (friendClient != null)
  724. {
  725. bool onlineBitChanged = ((rights ^ userFlags) & (int)FriendRights.CanSeeOnline) != 0;
  726. if (onlineBitChanged)
  727. {
  728. if ((rights & (int)FriendRights.CanSeeOnline) == 1)
  729. friendClient.SendAgentOnline(new UUID[] { userID });
  730. else
  731. friendClient.SendAgentOffline(new UUID[] { userID });
  732. }
  733. else
  734. {
  735. bool canEditObjectsChanged = ((rights ^ userFlags) & (int)FriendRights.CanModifyObjects) != 0;
  736. if (canEditObjectsChanged)
  737. friendClient.SendChangeUserRights(userID, friendID, rights);
  738. }
  739. // Update local cache
  740. UpdateLocalCache(userID, friendID, rights);
  741. return true;
  742. }
  743. return false;
  744. }
  745. public bool LocalStatusNotification(UUID userID, UUID friendID, bool online)
  746. {
  747. //m_log.DebugFormat("[FRIENDS]: Local Status Notify {0} that user {1} is {2}", friendID, userID, online);
  748. IClientAPI friendClient = LocateClientObject(friendID);
  749. if (friendClient != null)
  750. {
  751. // the friend in this sim as root agent
  752. if (online)
  753. friendClient.SendAgentOnline(new UUID[] { userID });
  754. else
  755. friendClient.SendAgentOffline(new UUID[] { userID });
  756. // we're done
  757. return true;
  758. }
  759. return false;
  760. }
  761. #endregion
  762. #region Get / Set friends in several flavours
  763. public FriendInfo[] GetFriendsFromCache(UUID userID)
  764. {
  765. UserFriendData friendsData;
  766. lock (m_Friends)
  767. {
  768. if (m_Friends.TryGetValue(userID, out friendsData))
  769. return friendsData.Friends;
  770. }
  771. return EMPTY_FRIENDS;
  772. }
  773. /// <summary>
  774. /// Update local cache only
  775. /// </summary>
  776. /// <param name="userID"></param>
  777. /// <param name="friendID"></param>
  778. /// <param name="rights"></param>
  779. protected void UpdateLocalCache(UUID userID, UUID friendID, int rights)
  780. {
  781. // Update local cache
  782. lock (m_Friends)
  783. {
  784. FriendInfo[] friends = GetFriendsFromCache(friendID);
  785. FriendInfo finfo = GetFriend(friends, userID);
  786. finfo.TheirFlags = rights;
  787. }
  788. }
  789. public virtual FriendInfo[] GetFriendsFromService(IClientAPI client)
  790. {
  791. return FriendsService.GetFriends(client.AgentId);
  792. }
  793. protected void RecacheFriends(IClientAPI client)
  794. {
  795. // FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event
  796. // is on the critical path for transferring an avatar from one region to another.
  797. UUID agentID = client.AgentId;
  798. lock (m_Friends)
  799. {
  800. UserFriendData friendsData;
  801. if (m_Friends.TryGetValue(agentID, out friendsData))
  802. friendsData.Friends = GetFriendsFromService(client);
  803. }
  804. }
  805. public bool AreFriendsCached(UUID userID)
  806. {
  807. lock (m_Friends)
  808. return m_Friends.ContainsKey(userID);
  809. }
  810. protected virtual bool StoreRights(UUID agentID, UUID friendID, int rights)
  811. {
  812. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), rights);
  813. return true;
  814. }
  815. protected virtual void StoreBackwards(UUID friendID, UUID agentID)
  816. {
  817. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0);
  818. }
  819. protected virtual void StoreFriendships(UUID agentID, UUID friendID)
  820. {
  821. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), (int)FriendRights.CanSeeOnline);
  822. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), (int)FriendRights.CanSeeOnline);
  823. }
  824. protected virtual bool DeleteFriendship(UUID agentID, UUID exfriendID)
  825. {
  826. FriendsService.Delete(agentID, exfriendID.ToString());
  827. FriendsService.Delete(exfriendID, agentID.ToString());
  828. return true;
  829. }
  830. #endregion
  831. }
  832. }