opensim – Blame information for rev 1
?pathlinks?
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 | } |