opensim – Blame information for rev 1

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