FriendsModule.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  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 = "User";
  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 = "User";
  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. List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend);
  424. List<string> remoteFriendStringIds = new List<string>();
  425. foreach (string friendStringId in friendStringIds)
  426. {
  427. UUID friendUuid;
  428. if (UUID.TryParse(friendStringId, out friendUuid))
  429. {
  430. if (LocalStatusNotification(userID, friendUuid, online))
  431. continue;
  432. remoteFriendStringIds.Add(friendStringId);
  433. }
  434. else
  435. {
  436. m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friendStringId);
  437. }
  438. }
  439. // We do this regrouping so that we can efficiently send a single request rather than one for each
  440. // friend in what may be a very large friends list.
  441. PresenceInfo[] friendSessions = PresenceService.GetAgents(remoteFriendStringIds.ToArray());
  442. foreach (PresenceInfo friendSession in friendSessions)
  443. {
  444. // let's guard against sessions-gone-bad
  445. if (friendSession.RegionID != UUID.Zero)
  446. {
  447. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  448. //m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName);
  449. m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online);
  450. }
  451. }
  452. }
  453. protected virtual void OnInstantMessage(IClientAPI client, GridInstantMessage im)
  454. {
  455. if ((InstantMessageDialog)im.dialog == InstantMessageDialog.FriendshipOffered)
  456. {
  457. // we got a friendship offer
  458. UUID principalID = new UUID(im.fromAgentID);
  459. UUID friendID = new UUID(im.toAgentID);
  460. m_log.DebugFormat("[FRIENDS]: {0} ({1}) offered friendship to {2} ({3})", principalID, client.FirstName + client.LastName, friendID, im.fromAgentName);
  461. // Check that the friendship doesn't exist yet
  462. FriendInfo[] finfos = GetFriendsFromCache(principalID);
  463. if (finfos != null)
  464. {
  465. FriendInfo f = GetFriend(finfos, friendID);
  466. if (f != null)
  467. {
  468. client.SendAgentAlertMessage("This person is already your friend. Please delete it first if you want to reestablish the friendship.", false);
  469. return;
  470. }
  471. }
  472. // This user wants to be friends with the other user.
  473. // Let's add the relation backwards, in case the other is not online
  474. StoreBackwards(friendID, principalID);
  475. // Now let's ask the other user to be friends with this user
  476. ForwardFriendshipOffer(principalID, friendID, im);
  477. }
  478. }
  479. protected virtual bool ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im)
  480. {
  481. // !!!!!!!! This is a hack so that we don't have to keep state (transactionID/imSessionID)
  482. // We stick this agent's ID as imSession, so that it's directly available on the receiving end
  483. im.imSessionID = im.fromAgentID;
  484. im.fromAgentName = GetFriendshipRequesterName(agentID);
  485. // Try the local sim
  486. if (LocalFriendshipOffered(friendID, im))
  487. return true;
  488. // The prospective friend is not here [as root]. Let's forward.
  489. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  490. if (friendSessions != null && friendSessions.Length > 0)
  491. {
  492. PresenceInfo friendSession = friendSessions[0];
  493. if (friendSession != null)
  494. {
  495. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  496. m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message);
  497. return true;
  498. }
  499. }
  500. // If the prospective friend is not online, he'll get the message upon login.
  501. return false;
  502. }
  503. protected virtual string GetFriendshipRequesterName(UUID agentID)
  504. {
  505. UserAccount account = UserAccountService.GetUserAccount(UUID.Zero, agentID);
  506. return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName;
  507. }
  508. protected virtual void OnApproveFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  509. {
  510. m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", client.AgentId, friendID);
  511. AddFriendship(client, friendID);
  512. }
  513. public void AddFriendship(IClientAPI client, UUID friendID)
  514. {
  515. StoreFriendships(client.AgentId, friendID);
  516. ICallingCardModule ccm = client.Scene.RequestModuleInterface<ICallingCardModule>();
  517. if (ccm != null)
  518. {
  519. ccm.CreateCallingCard(client.AgentId, friendID, UUID.Zero);
  520. }
  521. // Update the local cache.
  522. RecacheFriends(client);
  523. //
  524. // Notify the friend
  525. //
  526. // Try Local
  527. if (LocalFriendshipApproved(client.AgentId, client.Name, friendID))
  528. {
  529. client.SendAgentOnline(new UUID[] { friendID });
  530. return;
  531. }
  532. // The friend is not here
  533. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  534. if (friendSessions != null && friendSessions.Length > 0)
  535. {
  536. PresenceInfo friendSession = friendSessions[0];
  537. if (friendSession != null)
  538. {
  539. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  540. m_FriendsSimConnector.FriendshipApproved(region, client.AgentId, client.Name, friendID);
  541. client.SendAgentOnline(new UUID[] { friendID });
  542. }
  543. }
  544. }
  545. private void OnDenyFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  546. {
  547. m_log.DebugFormat("[FRIENDS]: {0} denied friendship to {1}", client.AgentId, friendID);
  548. DeleteFriendship(client.AgentId, friendID);
  549. //
  550. // Notify the friend
  551. //
  552. // Try local
  553. if (LocalFriendshipDenied(client.AgentId, client.Name, friendID))
  554. return;
  555. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  556. if (friendSessions != null && friendSessions.Length > 0)
  557. {
  558. PresenceInfo friendSession = friendSessions[0];
  559. if (friendSession != null)
  560. {
  561. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  562. if (region != null)
  563. m_FriendsSimConnector.FriendshipDenied(region, client.AgentId, client.Name, friendID);
  564. else
  565. m_log.WarnFormat("[FRIENDS]: Could not find region {0} in locating {1}", friendSession.RegionID, friendID);
  566. }
  567. }
  568. }
  569. public void RemoveFriendship(IClientAPI client, UUID exfriendID)
  570. {
  571. if (!DeleteFriendship(client.AgentId, exfriendID))
  572. client.SendAlertMessage("Unable to terminate friendship on this sim.");
  573. // Update local cache
  574. RecacheFriends(client);
  575. client.SendTerminateFriend(exfriendID);
  576. //
  577. // Notify the friend
  578. //
  579. // Try local
  580. if (LocalFriendshipTerminated(exfriendID))
  581. return;
  582. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() });
  583. if (friendSessions != null && friendSessions.Length > 0)
  584. {
  585. PresenceInfo friendSession = friendSessions[0];
  586. if (friendSession != null)
  587. {
  588. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  589. m_FriendsSimConnector.FriendshipTerminated(region, client.AgentId, exfriendID);
  590. }
  591. }
  592. }
  593. public void GrantRights(IClientAPI remoteClient, UUID friendID, int rights)
  594. {
  595. UUID requester = remoteClient.AgentId;
  596. m_log.DebugFormat(
  597. "[FRIENDS MODULE]: User {0} changing rights to {1} for friend {2}",
  598. requester, rights, friendID);
  599. FriendInfo[] friends = GetFriendsFromCache(requester);
  600. if (friends.Length == 0)
  601. {
  602. return;
  603. }
  604. // Let's find the friend in this user's friend list
  605. FriendInfo friend = GetFriend(friends, friendID);
  606. if (friend != null) // Found it
  607. {
  608. // Store it on the DB
  609. if (!StoreRights(requester, friendID, rights))
  610. {
  611. remoteClient.SendAlertMessage("Unable to grant rights.");
  612. return;
  613. }
  614. // Store it in the local cache
  615. int myFlags = friend.MyFlags;
  616. friend.MyFlags = rights;
  617. // Always send this back to the original client
  618. remoteClient.SendChangeUserRights(requester, friendID, rights);
  619. //
  620. // Notify the friend
  621. //
  622. // Try local
  623. if (LocalGrantRights(requester, friendID, myFlags, rights))
  624. return;
  625. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  626. if (friendSessions != null && friendSessions.Length > 0)
  627. {
  628. PresenceInfo friendSession = friendSessions[0];
  629. if (friendSession != null)
  630. {
  631. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  632. // TODO: You might want to send the delta to save the lookup
  633. // on the other end!!
  634. m_FriendsSimConnector.GrantRights(region, requester, friendID, myFlags, rights);
  635. }
  636. }
  637. }
  638. else
  639. {
  640. m_log.DebugFormat("[FRIENDS MODULE]: friend {0} not found for {1}", friendID, requester);
  641. }
  642. }
  643. protected virtual FriendInfo GetFriend(FriendInfo[] friends, UUID friendID)
  644. {
  645. foreach (FriendInfo fi in friends)
  646. {
  647. if (fi.Friend == friendID.ToString())
  648. return fi;
  649. }
  650. return null;
  651. }
  652. #region Local
  653. public virtual bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
  654. {
  655. IClientAPI friendClient = LocateClientObject(toID);
  656. if (friendClient != null)
  657. {
  658. // the prospective friend in this sim as root agent
  659. friendClient.SendInstantMessage(im);
  660. // we're done
  661. return true;
  662. }
  663. return false;
  664. }
  665. public bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID)
  666. {
  667. IClientAPI friendClient = LocateClientObject(friendID);
  668. if (friendClient != null)
  669. {
  670. // the prospective friend in this sim as root agent
  671. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  672. (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, userID.ToString(), false, Vector3.Zero);
  673. friendClient.SendInstantMessage(im);
  674. ICallingCardModule ccm = friendClient.Scene.RequestModuleInterface<ICallingCardModule>();
  675. if (ccm != null)
  676. {
  677. ccm.CreateCallingCard(friendID, userID, UUID.Zero);
  678. }
  679. // Update the local cache
  680. RecacheFriends(friendClient);
  681. // we're done
  682. return true;
  683. }
  684. return false;
  685. }
  686. public bool LocalFriendshipDenied(UUID userID, string userName, UUID friendID)
  687. {
  688. IClientAPI friendClient = LocateClientObject(friendID);
  689. if (friendClient != null)
  690. {
  691. // the prospective friend in this sim as root agent
  692. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  693. (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, userID.ToString(), false, Vector3.Zero);
  694. friendClient.SendInstantMessage(im);
  695. // we're done
  696. return true;
  697. }
  698. return false;
  699. }
  700. public bool LocalFriendshipTerminated(UUID exfriendID)
  701. {
  702. IClientAPI friendClient = LocateClientObject(exfriendID);
  703. if (friendClient != null)
  704. {
  705. // the friend in this sim as root agent
  706. friendClient.SendTerminateFriend(exfriendID);
  707. // update local cache
  708. RecacheFriends(friendClient);
  709. // we're done
  710. return true;
  711. }
  712. return false;
  713. }
  714. public bool LocalGrantRights(UUID userID, UUID friendID, int userFlags, int rights)
  715. {
  716. IClientAPI friendClient = LocateClientObject(friendID);
  717. if (friendClient != null)
  718. {
  719. bool onlineBitChanged = ((rights ^ userFlags) & (int)FriendRights.CanSeeOnline) != 0;
  720. if (onlineBitChanged)
  721. {
  722. if ((rights & (int)FriendRights.CanSeeOnline) == 1)
  723. friendClient.SendAgentOnline(new UUID[] { userID });
  724. else
  725. friendClient.SendAgentOffline(new UUID[] { userID });
  726. }
  727. else
  728. {
  729. bool canEditObjectsChanged = ((rights ^ userFlags) & (int)FriendRights.CanModifyObjects) != 0;
  730. if (canEditObjectsChanged)
  731. friendClient.SendChangeUserRights(userID, friendID, rights);
  732. }
  733. // Update local cache
  734. UpdateLocalCache(userID, friendID, rights);
  735. return true;
  736. }
  737. return false;
  738. }
  739. public bool LocalStatusNotification(UUID userID, UUID friendID, bool online)
  740. {
  741. //m_log.DebugFormat("[FRIENDS]: Local Status Notify {0} that user {1} is {2}", friendID, userID, online);
  742. IClientAPI friendClient = LocateClientObject(friendID);
  743. if (friendClient != null)
  744. {
  745. // the friend in this sim as root agent
  746. if (online)
  747. friendClient.SendAgentOnline(new UUID[] { userID });
  748. else
  749. friendClient.SendAgentOffline(new UUID[] { userID });
  750. // we're done
  751. return true;
  752. }
  753. return false;
  754. }
  755. #endregion
  756. #region Get / Set friends in several flavours
  757. public FriendInfo[] GetFriendsFromCache(UUID userID)
  758. {
  759. UserFriendData friendsData;
  760. lock (m_Friends)
  761. {
  762. if (m_Friends.TryGetValue(userID, out friendsData))
  763. return friendsData.Friends;
  764. }
  765. return EMPTY_FRIENDS;
  766. }
  767. /// <summary>
  768. /// Update local cache only
  769. /// </summary>
  770. /// <param name="userID"></param>
  771. /// <param name="friendID"></param>
  772. /// <param name="rights"></param>
  773. protected void UpdateLocalCache(UUID userID, UUID friendID, int rights)
  774. {
  775. // Update local cache
  776. lock (m_Friends)
  777. {
  778. FriendInfo[] friends = GetFriendsFromCache(friendID);
  779. FriendInfo finfo = GetFriend(friends, userID);
  780. finfo.TheirFlags = rights;
  781. }
  782. }
  783. public virtual FriendInfo[] GetFriendsFromService(IClientAPI client)
  784. {
  785. return FriendsService.GetFriends(client.AgentId);
  786. }
  787. protected void RecacheFriends(IClientAPI client)
  788. {
  789. // FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event
  790. // is on the critical path for transferring an avatar from one region to another.
  791. UUID agentID = client.AgentId;
  792. lock (m_Friends)
  793. {
  794. UserFriendData friendsData;
  795. if (m_Friends.TryGetValue(agentID, out friendsData))
  796. friendsData.Friends = GetFriendsFromService(client);
  797. }
  798. }
  799. public bool AreFriendsCached(UUID userID)
  800. {
  801. lock (m_Friends)
  802. return m_Friends.ContainsKey(userID);
  803. }
  804. protected virtual bool StoreRights(UUID agentID, UUID friendID, int rights)
  805. {
  806. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), rights);
  807. return true;
  808. }
  809. protected virtual void StoreBackwards(UUID friendID, UUID agentID)
  810. {
  811. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0);
  812. }
  813. protected virtual void StoreFriendships(UUID agentID, UUID friendID)
  814. {
  815. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), (int)FriendRights.CanSeeOnline);
  816. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), (int)FriendRights.CanSeeOnline);
  817. }
  818. protected virtual bool DeleteFriendship(UUID agentID, UUID exfriendID)
  819. {
  820. FriendsService.Delete(agentID, exfriendID.ToString());
  821. FriendsService.Delete(exfriendID, agentID.ToString());
  822. return true;
  823. }
  824. #endregion
  825. }
  826. }