FriendsModule.cs 39 KB

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