FriendsModule.cs 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085
  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.AddSimpleStreamHandler(new FriendsSimpleRequestHandler(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.m_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. if (client is INPC)
  234. return;
  235. client.OnApproveFriendRequest += OnApproveFriendRequest;
  236. client.OnDenyFriendRequest += OnDenyFriendRequest;
  237. client.OnTerminateFriendship += RemoveFriendship;
  238. client.OnGrantUserRights += GrantRights;
  239. client.OnFindAgent += FindFriend;
  240. // We need to cache information for child agents as well as root agents so that friend edit/move/delete
  241. // permissions will work across borders where both regions are on different simulators.
  242. //
  243. // Do not do this asynchronously. If we do, then subsequent code can outrace CacheFriends() and
  244. // return misleading results from the still empty friends cache.
  245. // If we absolutely need to do this asynchronously, then a signalling mechanism is needed so that calls
  246. // to GetFriends() will wait until CacheFriends() completes. Locks are insufficient.
  247. CacheFriends(client);
  248. }
  249. /// <summary>
  250. /// Cache the friends list or increment the refcount for the existing friends list.
  251. /// </summary>
  252. /// <param name="client">
  253. /// </param>
  254. /// <returns>
  255. /// Returns true if the list was fetched, false if it wasn't
  256. /// </returns>
  257. protected virtual bool CacheFriends(IClientAPI client)
  258. {
  259. UUID agentID = client.AgentId;
  260. lock (m_Friends)
  261. {
  262. UserFriendData friendsData;
  263. if (m_Friends.TryGetValue(agentID, out friendsData))
  264. {
  265. friendsData.Refcount++;
  266. return false;
  267. }
  268. else
  269. {
  270. friendsData = new UserFriendData();
  271. friendsData.PrincipalID = agentID;
  272. friendsData.Friends = GetFriendsFromService(client);
  273. friendsData.Refcount = 1;
  274. m_Friends[agentID] = friendsData;
  275. return true;
  276. }
  277. }
  278. }
  279. private void OnClientClosed(UUID agentID, Scene scene)
  280. {
  281. ScenePresence sp = scene.GetScenePresence(agentID);
  282. if (sp != null && !sp.IsChildAgent)
  283. {
  284. // do this for root agents closing out
  285. StatusChange(agentID, false);
  286. }
  287. lock (m_Friends)
  288. {
  289. UserFriendData friendsData;
  290. if (m_Friends.TryGetValue(agentID, out friendsData))
  291. {
  292. friendsData.Refcount--;
  293. if (friendsData.Refcount <= 0)
  294. m_Friends.Remove(agentID);
  295. }
  296. }
  297. }
  298. private void OnClientLogin(IClientAPI client)
  299. {
  300. UUID agentID = client.AgentId;
  301. //m_log.DebugFormat("[XXX]: OnClientLogin!");
  302. // Register that we need to send this user's status to friends. This can only be done
  303. // once the client becomes a Root Agent, because as part of sending out the presence
  304. // we also get back the presence of the HG friends, and we need to send that to the
  305. // client, but that can only be done when the client is a Root Agent.
  306. lock (m_NeedsToNotifyStatus)
  307. m_NeedsToNotifyStatus.Add(agentID);
  308. // Register that we need to send the list of online friends to this user
  309. lock (m_NeedsListOfOnlineFriends)
  310. m_NeedsListOfOnlineFriends.Add(agentID);
  311. }
  312. public void IsNowRoot(ScenePresence sp)
  313. {
  314. OnMakeRootAgent(sp);
  315. }
  316. public virtual bool SendFriendsOnlineIfNeeded(IClientAPI client)
  317. {
  318. if (client == null)
  319. return false;
  320. UUID agentID = client.AgentId;
  321. // Check if the online friends list is needed
  322. lock (m_NeedsListOfOnlineFriends)
  323. {
  324. if (!m_NeedsListOfOnlineFriends.Remove(agentID))
  325. return false;
  326. }
  327. // Send the friends online
  328. List<UUID> online = GetOnlineFriends(agentID);
  329. if (online.Count > 0)
  330. client.SendAgentOnline(online.ToArray());
  331. // Send outstanding friendship offers
  332. List<string> outstanding = new List<string>();
  333. FriendInfo[] friends = GetFriendsFromCache(agentID);
  334. foreach (FriendInfo fi in friends)
  335. {
  336. if (fi.TheirFlags == -1)
  337. outstanding.Add(fi.Friend);
  338. }
  339. GridInstantMessage im = new GridInstantMessage(client.Scene, UUID.Zero, String.Empty, agentID, (byte)InstantMessageDialog.FriendshipOffered,
  340. "Will you be my friend?", true, Vector3.Zero);
  341. foreach (string fid in outstanding)
  342. {
  343. UUID fromAgentID;
  344. string firstname = "Unknown", lastname = "UserFMSFOIN";
  345. if (!GetAgentInfo(client.Scene.RegionInfo.ScopeID, fid, out fromAgentID, out firstname, out lastname))
  346. {
  347. m_log.DebugFormat("[FRIENDS MODULE]: skipping malformed friend {0}", fid);
  348. continue;
  349. }
  350. im.offline = 0;
  351. im.fromAgentID = fromAgentID.Guid;
  352. im.fromAgentName = firstname + " " + lastname;
  353. im.imSessionID = im.fromAgentID;
  354. im.message = FriendshipMessage(fid);
  355. LocalFriendshipOffered(agentID, im);
  356. }
  357. return true;
  358. }
  359. protected virtual string FriendshipMessage(string friendID)
  360. {
  361. return "Will you be my friend?";
  362. }
  363. protected virtual bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last)
  364. {
  365. first = "Unknown"; last = "UserFMGAI";
  366. if (!UUID.TryParse(fid, out agentID))
  367. return false;
  368. UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(scopeID, agentID);
  369. if (account != null)
  370. {
  371. first = account.FirstName;
  372. last = account.LastName;
  373. }
  374. return true;
  375. }
  376. List<UUID> GetOnlineFriends(UUID userID)
  377. {
  378. List<string> friendList = new List<string>();
  379. FriendInfo[] friends = GetFriendsFromCache(userID);
  380. foreach (FriendInfo fi in friends)
  381. {
  382. if (((fi.TheirFlags & (int)FriendRights.CanSeeOnline) != 0) && (fi.TheirFlags != -1))
  383. friendList.Add(fi.Friend);
  384. }
  385. List<UUID> online = new List<UUID>();
  386. if (friendList.Count > 0)
  387. GetOnlineFriends(userID, friendList, online);
  388. // m_log.DebugFormat(
  389. // "[FRIENDS MODULE]: User {0} has {1} friends online", userID, online.Count);
  390. return online;
  391. }
  392. protected virtual void GetOnlineFriends(UUID userID, List<string> friendList, /*collector*/ List<UUID> online)
  393. {
  394. // m_log.DebugFormat(
  395. // "[FRIENDS MODULE]: Looking for online presence of {0} users for {1}", friendList.Count, userID);
  396. PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray());
  397. foreach (PresenceInfo pi in presence)
  398. {
  399. UUID presenceID;
  400. if (UUID.TryParse(pi.UserID, out presenceID))
  401. online.Add(presenceID);
  402. }
  403. }
  404. /// <summary>
  405. /// Find the client for a ID
  406. /// </summary>
  407. public IClientAPI LocateClientObject(UUID agentID)
  408. {
  409. lock (m_Scenes)
  410. {
  411. foreach (Scene scene in m_Scenes)
  412. {
  413. ScenePresence presence = scene.GetScenePresence(agentID);
  414. if (presence != null && !presence.IsDeleted && !presence.IsChildAgent)
  415. return presence.ControllingClient;
  416. }
  417. }
  418. return null;
  419. }
  420. /// <summary>
  421. /// Caller beware! Call this only for root agents.
  422. /// </summary>
  423. /// <param name="agentID"></param>
  424. /// <param name="online"></param>
  425. private void StatusChange(UUID agentID, bool online)
  426. {
  427. FriendInfo[] friends = GetFriendsFromCache(agentID);
  428. if (friends.Length > 0)
  429. {
  430. List<FriendInfo> friendList = new List<FriendInfo>();
  431. foreach (FriendInfo fi in friends)
  432. {
  433. if (((fi.MyFlags & (int)FriendRights.CanSeeOnline) != 0) && (fi.TheirFlags != -1))
  434. friendList.Add(fi);
  435. }
  436. if(friendList.Count > 0)
  437. {
  438. Util.FireAndForget(
  439. delegate
  440. {
  441. // m_log.DebugFormat(
  442. // "[FRIENDS MODULE]: Notifying {0} friends of {1} of online status {2}",
  443. // friendList.Count, agentID, online);
  444. // Notify about this user status
  445. StatusNotify(friendList, agentID, online);
  446. }, null, "FriendsModule.StatusChange"
  447. );
  448. }
  449. }
  450. }
  451. protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online)
  452. {
  453. //m_log.DebugFormat("[FRIENDS]: Entering StatusNotify for {0}", userID);
  454. List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend);
  455. List<string> remoteFriendStringIds = new List<string>();
  456. foreach (string friendStringId in friendStringIds)
  457. {
  458. UUID friendUuid;
  459. if (UUID.TryParse(friendStringId, out friendUuid))
  460. {
  461. if (LocalStatusNotification(userID, friendUuid, online))
  462. continue;
  463. remoteFriendStringIds.Add(friendStringId);
  464. }
  465. else
  466. {
  467. m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friendStringId);
  468. }
  469. }
  470. // We do this regrouping so that we can efficiently send a single request rather than one for each
  471. // friend in what may be a very large friends list.
  472. PresenceInfo[] friendSessions = PresenceService.GetAgents(remoteFriendStringIds.ToArray());
  473. if(friendSessions == null)
  474. return;
  475. foreach (PresenceInfo friendSession in friendSessions)
  476. {
  477. // let's guard against sessions-gone-bad
  478. if (friendSession != null && !friendSession.RegionID.IsZero())
  479. {
  480. //m_log.DebugFormat("[FRIENDS]: Get region {0}", friendSession.RegionID);
  481. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  482. if (region != null)
  483. {
  484. m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online);
  485. }
  486. }
  487. //else
  488. // m_log.DebugFormat("[FRIENDS]: friend session is null or the region is UUID.Zero");
  489. }
  490. }
  491. protected virtual void OnInstantMessage(IClientAPI client, GridInstantMessage im)
  492. {
  493. if ((InstantMessageDialog)im.dialog == InstantMessageDialog.FriendshipOffered)
  494. {
  495. // we got a friendship offer
  496. UUID principalID = new UUID(im.fromAgentID);
  497. UUID friendID = new UUID(im.toAgentID);
  498. m_log.DebugFormat("[FRIENDS]: {0} ({1}) offered friendship to {2} ({3})", principalID, client.FirstName + client.LastName, friendID, im.fromAgentName);
  499. // Check that the friendship doesn't exist yet
  500. FriendInfo[] finfos = GetFriendsFromCache(principalID);
  501. if (finfos != null)
  502. {
  503. FriendInfo f = GetFriend(finfos, friendID);
  504. if (f != null)
  505. {
  506. client.SendAgentAlertMessage("This person is already your friend. Please delete it first if you want to reestablish the friendship.", false);
  507. return;
  508. }
  509. }
  510. // This user wants to be friends with the other user.
  511. // Let's add the relation backwards, in case the other is not online
  512. StoreBackwards(friendID, principalID);
  513. // Now let's ask the other user to be friends with this user
  514. ForwardFriendshipOffer(principalID, friendID, im);
  515. }
  516. }
  517. protected virtual bool ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im)
  518. {
  519. // !!!!!!!! This is a hack so that we don't have to keep state (transactionID/imSessionID)
  520. // We stick this agent's ID as imSession, so that it's directly available on the receiving end
  521. im.imSessionID = im.fromAgentID;
  522. im.fromAgentName = GetFriendshipRequesterName(agentID);
  523. // Try the local sim
  524. if (LocalFriendshipOffered(friendID, im))
  525. return true;
  526. // The prospective friend is not here [as root]. Let's forward.
  527. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  528. if (friendSessions != null && friendSessions.Length > 0)
  529. {
  530. PresenceInfo friendSession = friendSessions[0];
  531. if (friendSession != null)
  532. {
  533. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  534. if(region != null)
  535. {
  536. m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message);
  537. return true;
  538. }
  539. }
  540. }
  541. // If the prospective friend is not online, he'll get the message upon login.
  542. return false;
  543. }
  544. protected virtual string GetFriendshipRequesterName(UUID agentID)
  545. {
  546. UserAccount account = UserAccountService.GetUserAccount(UUID.Zero, agentID);
  547. return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName;
  548. }
  549. protected virtual void OnApproveFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  550. {
  551. m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", client.AgentId, friendID);
  552. AddFriendship(client, friendID);
  553. }
  554. public void AddFriendship(IClientAPI client, UUID friendID)
  555. {
  556. StoreFriendships(client.AgentId, friendID);
  557. ICallingCardModule ccm = client.Scene.RequestModuleInterface<ICallingCardModule>();
  558. if (ccm != null)
  559. {
  560. ccm.CreateCallingCard(client.AgentId, friendID, UUID.Zero);
  561. }
  562. // Update the local cache.
  563. RecacheFriends(client);
  564. //
  565. // Notify the friend
  566. //
  567. // Try Local
  568. if (LocalFriendshipApproved(client.AgentId, client.Name, friendID))
  569. {
  570. client.SendAgentOnline(new UUID[] { friendID });
  571. return;
  572. }
  573. // The friend is not here
  574. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  575. if (friendSessions != null && friendSessions.Length > 0)
  576. {
  577. PresenceInfo friendSession = friendSessions[0];
  578. if (friendSession != null)
  579. {
  580. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  581. m_FriendsSimConnector.FriendshipApproved(region, client.AgentId, client.Name, friendID);
  582. client.SendAgentOnline(new UUID[] { friendID });
  583. }
  584. }
  585. }
  586. private void OnDenyFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  587. {
  588. m_log.DebugFormat("[FRIENDS]: {0} denied friendship to {1}", client.AgentId, friendID);
  589. DeleteFriendship(client.AgentId, friendID);
  590. //
  591. // Notify the friend
  592. //
  593. // Try local
  594. if (LocalFriendshipDenied(client.AgentId, client.Name, friendID))
  595. return;
  596. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.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. if (region != null)
  604. m_FriendsSimConnector.FriendshipDenied(region, client.AgentId, client.Name, friendID);
  605. else
  606. m_log.WarnFormat("[FRIENDS]: Could not find region {0} in locating {1}", friendSession.RegionID, friendID);
  607. }
  608. }
  609. }
  610. public void RemoveFriendship(IClientAPI client, UUID exfriendID)
  611. {
  612. if (!DeleteFriendship(client.AgentId, exfriendID))
  613. client.SendAlertMessage("Unable to terminate friendship on this sim.");
  614. // Update local cache
  615. RecacheFriends(client);
  616. client.SendTerminateFriend(exfriendID);
  617. //
  618. // Notify the friend
  619. //
  620. // Try local
  621. if (LocalFriendshipTerminated(client.AgentId, exfriendID))
  622. return;
  623. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() });
  624. if (friendSessions != null && friendSessions.Length > 0)
  625. {
  626. PresenceInfo friendSession = friendSessions[0];
  627. if (friendSession != null)
  628. {
  629. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  630. m_FriendsSimConnector.FriendshipTerminated(region, client.AgentId, exfriendID);
  631. }
  632. }
  633. }
  634. public void FindFriend(IClientAPI remoteClient,UUID HunterID ,UUID PreyID)
  635. {
  636. UUID requester = remoteClient.AgentId;
  637. if(requester != HunterID) // only allow client agent to be the hunter (?)
  638. return;
  639. FriendInfo[] friends = GetFriendsFromCache(requester);
  640. if (friends.Length == 0)
  641. return;
  642. FriendInfo friend = GetFriend(friends, PreyID);
  643. if (friend == null)
  644. return;
  645. if(friend.TheirFlags == -1 || (friend.TheirFlags & (int)FriendRights.CanSeeOnMap) == 0)
  646. return;
  647. Scene hunterScene = (Scene)remoteClient.Scene;
  648. if(hunterScene == null)
  649. return;
  650. // check local
  651. ScenePresence sp;
  652. double px;
  653. double py;
  654. if(hunterScene.TryGetScenePresence(PreyID, out sp))
  655. {
  656. if(sp == null)
  657. return;
  658. px = hunterScene.RegionInfo.WorldLocX + sp.AbsolutePosition.X;
  659. py = hunterScene.RegionInfo.WorldLocY + sp.AbsolutePosition.Y;
  660. remoteClient.SendFindAgent(HunterID, PreyID, px, py);
  661. return;
  662. }
  663. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { PreyID.ToString() });
  664. if (friendSessions == null || friendSessions.Length == 0)
  665. return;
  666. PresenceInfo friendSession = friendSessions[0];
  667. if (friendSession == null)
  668. return;
  669. GridRegion region = GridService.GetRegionByUUID(hunterScene.RegionInfo.ScopeID, friendSession.RegionID);
  670. if(region == null)
  671. return;
  672. // we don't have presence location so point to a standard region center for now
  673. px = region.RegionLocX + 128.0;
  674. py = region.RegionLocY + 128.0;
  675. remoteClient.SendFindAgent(HunterID, PreyID, px, py);
  676. }
  677. public void GrantRights(IClientAPI remoteClient, UUID friendID, int rights)
  678. {
  679. UUID requester = remoteClient.AgentId;
  680. m_log.DebugFormat(
  681. "[FRIENDS MODULE]: User {0} changing rights to {1} for friend {2}",
  682. requester, rights, friendID);
  683. FriendInfo[] friends = GetFriendsFromCache(requester);
  684. if (friends.Length == 0)
  685. {
  686. return;
  687. }
  688. // Let's find the friend in this user's friend list
  689. FriendInfo friend = GetFriend(friends, friendID);
  690. if (friend != null) // Found it
  691. {
  692. // Store it on service
  693. if (!StoreRights(requester, friendID, rights))
  694. {
  695. remoteClient.SendAlertMessage("Unable to grant rights.");
  696. return;
  697. }
  698. // Store it in the local cache
  699. int myFlags = friend.MyFlags;
  700. friend.MyFlags = rights;
  701. // Always send this back to the original client
  702. remoteClient.SendChangeUserRights(requester, friendID, rights);
  703. //
  704. // Notify the friend
  705. //
  706. // Try local
  707. if (LocalGrantRights(requester, friendID, myFlags, rights))
  708. return;
  709. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  710. if (friendSessions != null && friendSessions.Length > 0)
  711. {
  712. PresenceInfo friendSession = friendSessions[0];
  713. if (friendSession != null)
  714. {
  715. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  716. // TODO: You might want to send the delta to save the lookup
  717. // on the other end!!
  718. m_FriendsSimConnector.GrantRights(region, requester, friendID, myFlags, rights);
  719. }
  720. }
  721. }
  722. else
  723. {
  724. m_log.DebugFormat("[FRIENDS MODULE]: friend {0} not found for {1}", friendID, requester);
  725. }
  726. }
  727. protected virtual FriendInfo GetFriend(FriendInfo[] friends, UUID friendID)
  728. {
  729. foreach (FriendInfo fi in friends)
  730. {
  731. if (fi.Friend == friendID.ToString())
  732. return fi;
  733. }
  734. return null;
  735. }
  736. #region Local
  737. public virtual bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
  738. {
  739. IClientAPI friendClient = LocateClientObject(toID);
  740. if (friendClient != null)
  741. {
  742. // the prospective friend in this sim as root agent
  743. friendClient.SendInstantMessage(im);
  744. // we're done
  745. return true;
  746. }
  747. return false;
  748. }
  749. public bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID)
  750. {
  751. IClientAPI friendClient = LocateClientObject(friendID);
  752. if (friendClient != null)
  753. {
  754. // the prospective friend in this sim as root agent
  755. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  756. (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, userID.ToString(), false, Vector3.Zero);
  757. friendClient.SendInstantMessage(im);
  758. ICallingCardModule ccm = friendClient.Scene.RequestModuleInterface<ICallingCardModule>();
  759. if (ccm != null)
  760. {
  761. ccm.CreateCallingCard(friendID, userID, UUID.Zero);
  762. }
  763. // Update the local cache
  764. RecacheFriends(friendClient);
  765. // we're done
  766. return true;
  767. }
  768. return false;
  769. }
  770. public bool LocalFriendshipDenied(UUID userID, string userName, UUID friendID)
  771. {
  772. IClientAPI friendClient = LocateClientObject(friendID);
  773. if (friendClient != null)
  774. {
  775. // the prospective friend in this sim as root agent
  776. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  777. (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, userID.ToString(), false, Vector3.Zero);
  778. friendClient.SendInstantMessage(im);
  779. // we're done
  780. return true;
  781. }
  782. return false;
  783. }
  784. public bool LocalFriendshipTerminated(UUID userID, UUID exfriendID)
  785. {
  786. IClientAPI friendClient = LocateClientObject(exfriendID);
  787. if (friendClient != null)
  788. {
  789. // the friend in this sim as root agent
  790. friendClient.SendTerminateFriend(userID);
  791. // update local cache
  792. RecacheFriends(friendClient);
  793. // we're done
  794. return true;
  795. }
  796. return false;
  797. }
  798. public bool LocalGrantRights(UUID userID, UUID friendID, int oldRights, int newRights)
  799. {
  800. IClientAPI friendClient = LocateClientObject(friendID);
  801. if (friendClient != null)
  802. {
  803. int changedRights = newRights ^ oldRights;
  804. bool onlineBitChanged = (changedRights & (int)FriendRights.CanSeeOnline) != 0;
  805. if (onlineBitChanged)
  806. {
  807. if ((newRights & (int)FriendRights.CanSeeOnline) == 1)
  808. friendClient.SendAgentOnline(new UUID[] { userID });
  809. else
  810. friendClient.SendAgentOffline(new UUID[] { userID });
  811. }
  812. if(changedRights != 0)
  813. friendClient.SendChangeUserRights(userID, friendID, newRights);
  814. // Update local cache
  815. UpdateLocalCache(userID, friendID, newRights);
  816. return true;
  817. }
  818. return false;
  819. }
  820. public bool LocalStatusNotification(UUID userID, UUID friendID, bool online)
  821. {
  822. //m_log.DebugFormat("[FRIENDS]: Local Status Notify {0} that user {1} is {2}", friendID, userID, online);
  823. IClientAPI friendClient = LocateClientObject(friendID);
  824. if (friendClient != null)
  825. {
  826. // the friend in this sim as root agent
  827. if (online)
  828. friendClient.SendAgentOnline(new UUID[] { userID });
  829. else
  830. friendClient.SendAgentOffline(new UUID[] { userID });
  831. // we're done
  832. return true;
  833. }
  834. return false;
  835. }
  836. #endregion
  837. #region Get / Set friends in several flavours
  838. public FriendInfo[] GetFriendsFromCache(UUID userID)
  839. {
  840. UserFriendData friendsData;
  841. lock (m_Friends)
  842. {
  843. if (m_Friends.TryGetValue(userID, out friendsData))
  844. return friendsData.Friends;
  845. }
  846. return EMPTY_FRIENDS;
  847. }
  848. /// <summary>
  849. /// Update local cache only
  850. /// </summary>
  851. /// <param name="userID"></param>
  852. /// <param name="friendID"></param>
  853. /// <param name="rights"></param>
  854. protected void UpdateLocalCache(UUID userID, UUID friendID, int rights)
  855. {
  856. // Update local cache
  857. lock (m_Friends)
  858. {
  859. FriendInfo[] friends = GetFriendsFromCache(friendID);
  860. if(friends != EMPTY_FRIENDS)
  861. {
  862. FriendInfo finfo = GetFriend(friends, userID);
  863. if(finfo!= null)
  864. finfo.TheirFlags = rights;
  865. }
  866. }
  867. }
  868. public virtual FriendInfo[] GetFriendsFromService(IClientAPI client)
  869. {
  870. return FriendsService.GetFriends(client.AgentId);
  871. }
  872. protected void RecacheFriends(IClientAPI client)
  873. {
  874. // FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event
  875. // is on the critical path for transferring an avatar from one region to another.
  876. UUID agentID = client.AgentId;
  877. lock (m_Friends)
  878. {
  879. UserFriendData friendsData;
  880. if (m_Friends.TryGetValue(agentID, out friendsData))
  881. friendsData.Friends = GetFriendsFromService(client);
  882. }
  883. }
  884. public bool AreFriendsCached(UUID userID)
  885. {
  886. lock (m_Friends)
  887. return m_Friends.ContainsKey(userID);
  888. }
  889. protected virtual bool StoreRights(UUID agentID, UUID friendID, int rights)
  890. {
  891. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), rights);
  892. return true;
  893. }
  894. protected virtual void StoreBackwards(UUID friendID, UUID agentID)
  895. {
  896. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0);
  897. }
  898. protected virtual void StoreFriendships(UUID agentID, UUID friendID)
  899. {
  900. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), (int)FriendRights.CanSeeOnline);
  901. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), (int)FriendRights.CanSeeOnline);
  902. }
  903. protected virtual bool DeleteFriendship(UUID agentID, UUID exfriendID)
  904. {
  905. FriendsService.Delete(agentID, exfriendID.ToString());
  906. FriendsService.Delete(exfriendID, agentID.ToString());
  907. return true;
  908. }
  909. #endregion
  910. }
  911. }