FriendsModule.cs 37 KB

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