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