clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 clients that need to notify about their online status. 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_NeedsToNotifyStatus = new HashSet<UUID>();
101  
102 /// <summary>
103 /// Maintain a record of viewers that need to be sent notifications for friends that are online. This only
104 /// needs to be done on login. Subsequent online/offline friend changes are sent by a different mechanism.
105 /// </summary>
106 protected HashSet<UUID> m_NeedsListOfOnlineFriends = new HashSet<UUID>();
107  
108 protected IPresenceService PresenceService
109 {
110 get
111 {
112 if (m_PresenceService == null)
113 {
114 if (m_Scenes.Count > 0)
115 m_PresenceService = m_Scenes[0].RequestModuleInterface<IPresenceService>();
116 }
117  
118 return m_PresenceService;
119 }
120 }
121  
122 public IFriendsService FriendsService
123 {
124 get
125 {
126 if (m_FriendsService == null)
127 {
128 if (m_Scenes.Count > 0)
129 m_FriendsService = m_Scenes[0].RequestModuleInterface<IFriendsService>();
130 }
131  
132 return m_FriendsService;
133 }
134 }
135  
136 protected IGridService GridService
137 {
138 get { return m_Scenes[0].GridService; }
139 }
140  
141 public IUserAccountService UserAccountService
142 {
143 get { return m_Scenes[0].UserAccountService; }
144 }
145  
146 public IScene Scene
147 {
148 get
149 {
150 if (m_Scenes.Count > 0)
151 return m_Scenes[0];
152 else
153 return null;
154 }
155 }
156  
157 #region ISharedRegionModule
158 public void Initialise(IConfigSource config)
159 {
160 IConfig moduleConfig = config.Configs["Modules"];
161 if (moduleConfig != null)
162 {
163 string name = moduleConfig.GetString("FriendsModule", "FriendsModule");
164 if (name == Name)
165 {
166 InitModule(config);
167  
168 m_Enabled = true;
169 m_log.DebugFormat("[FRIENDS MODULE]: {0} enabled.", Name);
170 }
171 }
172 }
173  
174 protected virtual void InitModule(IConfigSource config)
175 {
176 IConfig friendsConfig = config.Configs["Friends"];
177 if (friendsConfig != null)
178 {
179 int mPort = friendsConfig.GetInt("Port", 0);
180  
181 string connector = friendsConfig.GetString("Connector", String.Empty);
182 Object[] args = new Object[] { config };
183  
184 m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(connector, args);
185 m_FriendsSimConnector = new FriendsSimConnector();
186  
187 // Instantiate the request handler
188 IHttpServer server = MainServer.GetHttpServer((uint)mPort);
189  
190 if (server != null)
191 server.AddStreamHandler(new FriendsRequestHandler(this));
192 }
193  
194 if (m_FriendsService == null)
195 {
196 m_log.Error("[FRIENDS]: No Connector defined in section Friends, or failed to load, cannot continue");
197 throw new Exception("Connector load error");
198 }
199 }
200  
201 public void PostInitialise()
202 {
203 }
204  
205 public void Close()
206 {
207 }
208  
209 public virtual void AddRegion(Scene scene)
210 {
211 if (!m_Enabled)
212 return;
213  
214 // m_log.DebugFormat("[FRIENDS MODULE]: AddRegion on {0}", Name);
215  
216 m_Scenes.Add(scene);
217 scene.RegisterModuleInterface<IFriendsModule>(this);
218  
219 scene.EventManager.OnNewClient += OnNewClient;
220 scene.EventManager.OnClientClosed += OnClientClosed;
221 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
222 scene.EventManager.OnClientLogin += OnClientLogin;
223 }
224  
225 public virtual void RegionLoaded(Scene scene) {}
226  
227 public void RemoveRegion(Scene scene)
228 {
229 if (!m_Enabled)
230 return;
231  
232 m_Scenes.Remove(scene);
233 }
234  
235 public virtual string Name
236 {
237 get { return "FriendsModule"; }
238 }
239  
240 public Type ReplaceableInterface
241 {
242 get { return null; }
243 }
244  
245 #endregion
246  
247 public virtual int GetRightsGrantedByFriend(UUID principalID, UUID friendID)
248 {
249 FriendInfo[] friends = GetFriendsFromCache(principalID);
250 FriendInfo finfo = GetFriend(friends, friendID);
251 if (finfo != null)
252 {
253 return finfo.TheirFlags;
254 }
255  
256 return 0;
257 }
258  
259 private void OnNewClient(IClientAPI client)
260 {
261 client.OnInstantMessage += OnInstantMessage;
262 client.OnApproveFriendRequest += OnApproveFriendRequest;
263 client.OnDenyFriendRequest += OnDenyFriendRequest;
264 client.OnTerminateFriendship += RemoveFriendship;
265 client.OnGrantUserRights += GrantRights;
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  
277 /// <summary>
278 /// Cache the friends list or increment the refcount for the existing friends list.
279 /// </summary>
280 /// <param name="client">
281 /// </param>
282 /// <returns>
283 /// Returns true if the list was fetched, false if it wasn't
284 /// </returns>
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  
470 /// <summary>
471 /// Find the client for a ID
472 /// </summary>
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  
488 /// <summary>
489 /// Caller beware! Call this only for root agents.
490 /// </summary>
491 /// <param name="agentID"></param>
492 /// <param name="online"></param>
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 }
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 GrantRights(IClientAPI remoteClient, UUID friendID, int rights)
731 {
732 UUID requester = remoteClient.AgentId;
733  
734 m_log.DebugFormat(
735 "[FRIENDS MODULE]: User {0} changing rights to {1} for friend {2}",
736 requester, rights, friendID);
737  
738 FriendInfo[] friends = GetFriendsFromCache(requester);
739 if (friends.Length == 0)
740 {
741 return;
742 }
743  
744 // Let's find the friend in this user's friend list
745 FriendInfo friend = GetFriend(friends, friendID);
746  
747 if (friend != null) // Found it
748 {
749 // Store it on the DB
750 if (!StoreRights(requester, friendID, rights))
751 {
752 remoteClient.SendAlertMessage("Unable to grant rights.");
753 return;
754 }
755  
756 // Store it in the local cache
757 int myFlags = friend.MyFlags;
758 friend.MyFlags = rights;
759  
760 // Always send this back to the original client
761 remoteClient.SendChangeUserRights(requester, friendID, rights);
762  
763 //
764 // Notify the friend
765 //
766  
767 // Try local
768 if (LocalGrantRights(requester, friendID, myFlags, rights))
769 return;
770  
771 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
772 if (friendSessions != null && friendSessions.Length > 0)
773 {
774 PresenceInfo friendSession = friendSessions[0];
775 if (friendSession != null)
776 {
777 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
778 // TODO: You might want to send the delta to save the lookup
779 // on the other end!!
780 m_FriendsSimConnector.GrantRights(region, requester, friendID, myFlags, rights);
781 }
782 }
783 }
784 else
785 {
786 m_log.DebugFormat("[FRIENDS MODULE]: friend {0} not found for {1}", friendID, requester);
787 }
788 }
789  
790 protected virtual FriendInfo GetFriend(FriendInfo[] friends, UUID friendID)
791 {
792 foreach (FriendInfo fi in friends)
793 {
794 if (fi.Friend == friendID.ToString())
795 return fi;
796 }
797 return null;
798 }
799  
800 #region Local
801  
802 public virtual bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
803 {
804 IClientAPI friendClient = LocateClientObject(toID);
805 if (friendClient != null)
806 {
807 // the prospective friend in this sim as root agent
808 friendClient.SendInstantMessage(im);
809 // we're done
810 return true;
811 }
812 return false;
813 }
814  
815 public bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID)
816 {
817 IClientAPI friendClient = LocateClientObject(friendID);
818 if (friendClient != null)
819 {
820 // the prospective friend in this sim as root agent
821 GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
822 (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, userID.ToString(), false, Vector3.Zero);
823 friendClient.SendInstantMessage(im);
824  
825 ICallingCardModule ccm = friendClient.Scene.RequestModuleInterface<ICallingCardModule>();
826 if (ccm != null)
827 {
828 ccm.CreateCallingCard(friendID, userID, UUID.Zero);
829 }
830  
831 // Update the local cache
832 RecacheFriends(friendClient);
833  
834 // we're done
835 return true;
836 }
837  
838 return false;
839 }
840  
841 public bool LocalFriendshipDenied(UUID userID, string userName, UUID friendID)
842 {
843 IClientAPI friendClient = LocateClientObject(friendID);
844 if (friendClient != null)
845 {
846 // the prospective friend in this sim as root agent
847 GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
848 (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, userID.ToString(), false, Vector3.Zero);
849 friendClient.SendInstantMessage(im);
850 // we're done
851 return true;
852 }
853  
854 return false;
855 }
856  
857 public bool LocalFriendshipTerminated(UUID userID, UUID exfriendID)
858 {
859 IClientAPI friendClient = LocateClientObject(exfriendID);
860 if (friendClient != null)
861 {
862 // the friend in this sim as root agent
863 friendClient.SendTerminateFriend(userID);
864 // update local cache
865 RecacheFriends(friendClient);
866 // we're done
867 return true;
868 }
869  
870 return false;
871 }
872  
873 public bool LocalGrantRights(UUID userID, UUID friendID, int userFlags, int rights)
874 {
875 IClientAPI friendClient = LocateClientObject(friendID);
876 if (friendClient != null)
877 {
878 bool onlineBitChanged = ((rights ^ userFlags) & (int)FriendRights.CanSeeOnline) != 0;
879 if (onlineBitChanged)
880 {
881 if ((rights & (int)FriendRights.CanSeeOnline) == 1)
882 friendClient.SendAgentOnline(new UUID[] { userID });
883 else
884 friendClient.SendAgentOffline(new UUID[] { userID });
885 }
886 else
887 {
888 bool canEditObjectsChanged = ((rights ^ userFlags) & (int)FriendRights.CanModifyObjects) != 0;
889 if (canEditObjectsChanged)
890 friendClient.SendChangeUserRights(userID, friendID, rights);
891 }
892  
893 // Update local cache
894 UpdateLocalCache(userID, friendID, rights);
895  
896 return true;
897 }
898  
899 return false;
900  
901 }
902  
903 public bool LocalStatusNotification(UUID userID, UUID friendID, bool online)
904 {
905 //m_log.DebugFormat("[FRIENDS]: Local Status Notify {0} that user {1} is {2}", friendID, userID, online);
906 IClientAPI friendClient = LocateClientObject(friendID);
907 if (friendClient != null)
908 {
909 // the friend in this sim as root agent
910 if (online)
911 friendClient.SendAgentOnline(new UUID[] { userID });
912 else
913 friendClient.SendAgentOffline(new UUID[] { userID });
914 // we're done
915 return true;
916 }
917  
918 return false;
919 }
920  
921 #endregion
922  
923 #region Get / Set friends in several flavours
924  
925 public FriendInfo[] GetFriendsFromCache(UUID userID)
926 {
927 UserFriendData friendsData;
928  
929 lock (m_Friends)
930 {
931 if (m_Friends.TryGetValue(userID, out friendsData))
932 return friendsData.Friends;
933 }
934  
935 return EMPTY_FRIENDS;
936 }
937  
938 /// <summary>
939 /// Update local cache only
940 /// </summary>
941 /// <param name="userID"></param>
942 /// <param name="friendID"></param>
943 /// <param name="rights"></param>
944 protected void UpdateLocalCache(UUID userID, UUID friendID, int rights)
945 {
946 // Update local cache
947 lock (m_Friends)
948 {
949 FriendInfo[] friends = GetFriendsFromCache(friendID);
950 FriendInfo finfo = GetFriend(friends, userID);
951 finfo.TheirFlags = rights;
952 }
953 }
954  
955 public virtual FriendInfo[] GetFriendsFromService(IClientAPI client)
956 {
957 return FriendsService.GetFriends(client.AgentId);
958 }
959  
960 protected void RecacheFriends(IClientAPI client)
961 {
962 // FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event
963 // is on the critical path for transferring an avatar from one region to another.
964 UUID agentID = client.AgentId;
965 lock (m_Friends)
966 {
967 UserFriendData friendsData;
968 if (m_Friends.TryGetValue(agentID, out friendsData))
969 friendsData.Friends = GetFriendsFromService(client);
970 }
971 }
972  
973 public bool AreFriendsCached(UUID userID)
974 {
975 lock (m_Friends)
976 return m_Friends.ContainsKey(userID);
977 }
978  
979 protected virtual bool StoreRights(UUID agentID, UUID friendID, int rights)
980 {
981 FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), rights);
982 return true;
983 }
984  
985 protected virtual void StoreBackwards(UUID friendID, UUID agentID)
986 {
987 FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0);
988 }
989  
990 protected virtual void StoreFriendships(UUID agentID, UUID friendID)
991 {
992 FriendsService.StoreFriend(agentID.ToString(), friendID.ToString(), (int)FriendRights.CanSeeOnline);
993 FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), (int)FriendRights.CanSeeOnline);
994 }
995  
996 protected virtual bool DeleteFriendship(UUID agentID, UUID exfriendID)
997 {
998 FriendsService.Delete(agentID, exfriendID.ToString());
999 FriendsService.Delete(exfriendID, agentID.ToString());
1000 return true;
1001 }
1002  
1003 #endregion
1004 }
1005 }