FriendsModule.cs 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  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.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.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 != UUID.Zero)
  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. m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message);
  535. return true;
  536. }
  537. }
  538. // If the prospective friend is not online, he'll get the message upon login.
  539. return false;
  540. }
  541. protected virtual string GetFriendshipRequesterName(UUID agentID)
  542. {
  543. UserAccount account = UserAccountService.GetUserAccount(UUID.Zero, agentID);
  544. return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName;
  545. }
  546. protected virtual void OnApproveFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  547. {
  548. m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", client.AgentId, friendID);
  549. AddFriendship(client, friendID);
  550. }
  551. public void AddFriendship(IClientAPI client, UUID friendID)
  552. {
  553. StoreFriendships(client.AgentId, friendID);
  554. ICallingCardModule ccm = client.Scene.RequestModuleInterface<ICallingCardModule>();
  555. if (ccm != null)
  556. {
  557. ccm.CreateCallingCard(client.AgentId, friendID, UUID.Zero);
  558. }
  559. // Update the local cache.
  560. RecacheFriends(client);
  561. //
  562. // Notify the friend
  563. //
  564. // Try Local
  565. if (LocalFriendshipApproved(client.AgentId, client.Name, friendID))
  566. {
  567. client.SendAgentOnline(new UUID[] { friendID });
  568. return;
  569. }
  570. // The friend is not here
  571. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  572. if (friendSessions != null && friendSessions.Length > 0)
  573. {
  574. PresenceInfo friendSession = friendSessions[0];
  575. if (friendSession != null)
  576. {
  577. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  578. m_FriendsSimConnector.FriendshipApproved(region, client.AgentId, client.Name, friendID);
  579. client.SendAgentOnline(new UUID[] { friendID });
  580. }
  581. }
  582. }
  583. private void OnDenyFriendRequest(IClientAPI client, UUID friendID, List<UUID> callingCardFolders)
  584. {
  585. m_log.DebugFormat("[FRIENDS]: {0} denied friendship to {1}", client.AgentId, friendID);
  586. DeleteFriendship(client.AgentId, friendID);
  587. //
  588. // Notify the friend
  589. //
  590. // Try local
  591. if (LocalFriendshipDenied(client.AgentId, client.Name, friendID))
  592. return;
  593. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  594. if (friendSessions != null && friendSessions.Length > 0)
  595. {
  596. PresenceInfo friendSession = friendSessions[0];
  597. if (friendSession != null)
  598. {
  599. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  600. if (region != null)
  601. m_FriendsSimConnector.FriendshipDenied(region, client.AgentId, client.Name, friendID);
  602. else
  603. m_log.WarnFormat("[FRIENDS]: Could not find region {0} in locating {1}", friendSession.RegionID, friendID);
  604. }
  605. }
  606. }
  607. public void RemoveFriendship(IClientAPI client, UUID exfriendID)
  608. {
  609. if (!DeleteFriendship(client.AgentId, exfriendID))
  610. client.SendAlertMessage("Unable to terminate friendship on this sim.");
  611. // Update local cache
  612. RecacheFriends(client);
  613. client.SendTerminateFriend(exfriendID);
  614. //
  615. // Notify the friend
  616. //
  617. // Try local
  618. if (LocalFriendshipTerminated(client.AgentId, exfriendID))
  619. return;
  620. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() });
  621. if (friendSessions != null && friendSessions.Length > 0)
  622. {
  623. PresenceInfo friendSession = friendSessions[0];
  624. if (friendSession != null)
  625. {
  626. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  627. m_FriendsSimConnector.FriendshipTerminated(region, client.AgentId, exfriendID);
  628. }
  629. }
  630. }
  631. public void FindFriend(IClientAPI remoteClient,UUID HunterID ,UUID PreyID)
  632. {
  633. UUID requester = remoteClient.AgentId;
  634. if(requester != HunterID) // only allow client agent to be the hunter (?)
  635. return;
  636. FriendInfo[] friends = GetFriendsFromCache(requester);
  637. if (friends.Length == 0)
  638. return;
  639. FriendInfo friend = GetFriend(friends, PreyID);
  640. if (friend == null)
  641. return;
  642. if(friend.TheirFlags == -1 || (friend.TheirFlags & (int)FriendRights.CanSeeOnMap) == 0)
  643. return;
  644. Scene hunterScene = (Scene)remoteClient.Scene;
  645. if(hunterScene == null)
  646. return;
  647. // check local
  648. ScenePresence sp;
  649. double px;
  650. double py;
  651. if(hunterScene.TryGetScenePresence(PreyID, out sp))
  652. {
  653. if(sp == null)
  654. return;
  655. px = hunterScene.RegionInfo.WorldLocX + sp.AbsolutePosition.X;
  656. py = hunterScene.RegionInfo.WorldLocY + sp.AbsolutePosition.Y;
  657. remoteClient.SendFindAgent(HunterID, PreyID, px, py);
  658. return;
  659. }
  660. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { PreyID.ToString() });
  661. if (friendSessions == null || friendSessions.Length == 0)
  662. return;
  663. PresenceInfo friendSession = friendSessions[0];
  664. if (friendSession == null)
  665. return;
  666. GridRegion region = GridService.GetRegionByUUID(hunterScene.RegionInfo.ScopeID, friendSession.RegionID);
  667. if(region == null)
  668. return;
  669. // we don't have presence location so point to a standard region center for now
  670. px = region.RegionLocX + 128.0;
  671. py = region.RegionLocY + 128.0;
  672. remoteClient.SendFindAgent(HunterID, PreyID, px, py);
  673. }
  674. public void GrantRights(IClientAPI remoteClient, UUID friendID, int rights)
  675. {
  676. UUID requester = remoteClient.AgentId;
  677. m_log.DebugFormat(
  678. "[FRIENDS MODULE]: User {0} changing rights to {1} for friend {2}",
  679. requester, rights, friendID);
  680. FriendInfo[] friends = GetFriendsFromCache(requester);
  681. if (friends.Length == 0)
  682. {
  683. return;
  684. }
  685. // Let's find the friend in this user's friend list
  686. FriendInfo friend = GetFriend(friends, friendID);
  687. if (friend != null) // Found it
  688. {
  689. // Store it on service
  690. if (!StoreRights(requester, friendID, rights))
  691. {
  692. remoteClient.SendAlertMessage("Unable to grant rights.");
  693. return;
  694. }
  695. // Store it in the local cache
  696. int myFlags = friend.MyFlags;
  697. friend.MyFlags = rights;
  698. // Always send this back to the original client
  699. remoteClient.SendChangeUserRights(requester, friendID, rights);
  700. //
  701. // Notify the friend
  702. //
  703. // Try local
  704. if (LocalGrantRights(requester, friendID, myFlags, rights))
  705. return;
  706. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
  707. if (friendSessions != null && friendSessions.Length > 0)
  708. {
  709. PresenceInfo friendSession = friendSessions[0];
  710. if (friendSession != null)
  711. {
  712. GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
  713. // TODO: You might want to send the delta to save the lookup
  714. // on the other end!!
  715. m_FriendsSimConnector.GrantRights(region, requester, friendID, myFlags, rights);
  716. }
  717. }
  718. }
  719. else
  720. {
  721. m_log.DebugFormat("[FRIENDS MODULE]: friend {0} not found for {1}", friendID, requester);
  722. }
  723. }
  724. protected virtual FriendInfo GetFriend(FriendInfo[] friends, UUID friendID)
  725. {
  726. foreach (FriendInfo fi in friends)
  727. {
  728. if (fi.Friend == friendID.ToString())
  729. return fi;
  730. }
  731. return null;
  732. }
  733. #region Local
  734. public virtual bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
  735. {
  736. IClientAPI friendClient = LocateClientObject(toID);
  737. if (friendClient != null)
  738. {
  739. // the prospective friend in this sim as root agent
  740. friendClient.SendInstantMessage(im);
  741. // we're done
  742. return true;
  743. }
  744. return false;
  745. }
  746. public bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID)
  747. {
  748. IClientAPI friendClient = LocateClientObject(friendID);
  749. if (friendClient != null)
  750. {
  751. // the prospective friend in this sim as root agent
  752. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  753. (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, userID.ToString(), false, Vector3.Zero);
  754. friendClient.SendInstantMessage(im);
  755. ICallingCardModule ccm = friendClient.Scene.RequestModuleInterface<ICallingCardModule>();
  756. if (ccm != null)
  757. {
  758. ccm.CreateCallingCard(friendID, userID, UUID.Zero);
  759. }
  760. // Update the local cache
  761. RecacheFriends(friendClient);
  762. // we're done
  763. return true;
  764. }
  765. return false;
  766. }
  767. public bool LocalFriendshipDenied(UUID userID, string userName, UUID friendID)
  768. {
  769. IClientAPI friendClient = LocateClientObject(friendID);
  770. if (friendClient != null)
  771. {
  772. // the prospective friend in this sim as root agent
  773. GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
  774. (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, userID.ToString(), false, Vector3.Zero);
  775. friendClient.SendInstantMessage(im);
  776. // we're done
  777. return true;
  778. }
  779. return false;
  780. }
  781. public bool LocalFriendshipTerminated(UUID userID, UUID exfriendID)
  782. {
  783. IClientAPI friendClient = LocateClientObject(exfriendID);
  784. if (friendClient != null)
  785. {
  786. // the friend in this sim as root agent
  787. friendClient.SendTerminateFriend(userID);
  788. // update local cache
  789. RecacheFriends(friendClient);
  790. // we're done
  791. return true;
  792. }
  793. return false;
  794. }
  795. public bool LocalGrantRights(UUID userID, UUID friendID, int oldRights, int newRights)
  796. {
  797. IClientAPI friendClient = LocateClientObject(friendID);
  798. if (friendClient != null)
  799. {
  800. int changedRights = newRights ^ oldRights;
  801. bool onlineBitChanged = (changedRights & (int)FriendRights.CanSeeOnline) != 0;
  802. if (onlineBitChanged)
  803. {
  804. if ((newRights & (int)FriendRights.CanSeeOnline) == 1)
  805. friendClient.SendAgentOnline(new UUID[] { userID });
  806. else
  807. friendClient.SendAgentOffline(new UUID[] { userID });
  808. }
  809. if(changedRights != 0)
  810. friendClient.SendChangeUserRights(userID, friendID, newRights);
  811. // Update local cache
  812. UpdateLocalCache(userID, friendID, newRights);
  813. return true;
  814. }
  815. return false;
  816. }
  817. public bool LocalStatusNotification(UUID userID, UUID friendID, bool online)
  818. {
  819. //m_log.DebugFormat("[FRIENDS]: Local Status Notify {0} that user {1} is {2}", friendID, userID, online);
  820. IClientAPI friendClient = LocateClientObject(friendID);
  821. if (friendClient != null)
  822. {
  823. // the friend in this sim as root agent
  824. if (online)
  825. friendClient.SendAgentOnline(new UUID[] { userID });
  826. else
  827. friendClient.SendAgentOffline(new UUID[] { userID });
  828. // we're done
  829. return true;
  830. }
  831. return false;
  832. }
  833. #endregion
  834. #region Get / Set friends in several flavours
  835. public FriendInfo[] GetFriendsFromCache(UUID userID)
  836. {
  837. UserFriendData friendsData;
  838. lock (m_Friends)
  839. {
  840. if (m_Friends.TryGetValue(userID, out friendsData))
  841. return friendsData.Friends;
  842. }
  843. return EMPTY_FRIENDS;
  844. }
  845. /// <summary>
  846. /// Update local cache only
  847. /// </summary>
  848. /// <param name="userID"></param>
  849. /// <param name="friendID"></param>
  850. /// <param name="rights"></param>
  851. protected void UpdateLocalCache(UUID userID, UUID friendID, int rights)
  852. {
  853. // Update local cache
  854. lock (m_Friends)
  855. {
  856. FriendInfo[] friends = GetFriendsFromCache(friendID);
  857. if(friends != EMPTY_FRIENDS)
  858. {
  859. FriendInfo finfo = GetFriend(friends, userID);
  860. if(finfo!= null)
  861. finfo.TheirFlags = rights;
  862. }
  863. }
  864. }
  865. public virtual FriendInfo[] GetFriendsFromService(IClientAPI client)
  866. {
  867. return FriendsService.GetFriends(client.AgentId);
  868. }
  869. protected void RecacheFriends(IClientAPI client)
  870. {
  871. // FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event
  872. // is on the critical path for transferring an avatar from one region to another.
  873. UUID agentID = client.AgentId;
  874. lock (m_Friends)
  875. {
  876. UserFriendData friendsData;
  877. if (m_Friends.TryGetValue(agentID, out friendsData))
  878. friendsData.Friends = GetFriendsFromService(client);
  879. }
  880. }
  881. public bool AreFriendsCached(UUID userID)
  882. {
  883. lock (m_Friends)
  884. return m_Friends.ContainsKey(userID);
  885. }
  886. protected virtual bool StoreRights(UUID agentID, UUID friendID, int rights)
  887. {
  888. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), rights);
  889. return true;
  890. }
  891. protected virtual void StoreBackwards(UUID friendID, UUID agentID)
  892. {
  893. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0);
  894. }
  895. protected virtual void StoreFriendships(UUID agentID, UUID friendID)
  896. {
  897. FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), (int)FriendRights.CanSeeOnline);
  898. FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), (int)FriendRights.CanSeeOnline);
  899. }
  900. protected virtual bool DeleteFriendship(UUID agentID, UUID exfriendID)
  901. {
  902. FriendsService.Delete(agentID, exfriendID.ToString());
  903. FriendsService.Delete(exfriendID, agentID.ToString());
  904. return true;
  905. }
  906. #endregion
  907. }
  908. }