FriendsModule.cs 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  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 && finfo.TheirFlags != -1)
  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 == -1 || (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. }