FriendsModule.cs 33 KB

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