aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs599
1 files changed, 599 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs
new file mode 100644
index 0000000..b0a7567
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs
@@ -0,0 +1,599 @@
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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using log4net;
33using Nini.Config;
34using Nwc.XmlRpc;
35using Mono.Addins;
36using OpenMetaverse;
37using OpenSim.Framework;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using OpenSim.Services.Connectors.Hypergrid;
42using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
43using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
44using GridRegion = OpenSim.Services.Interfaces.GridRegion;
45
46namespace OpenSim.Region.CoreModules.Avatar.Friends
47{
48 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
49 public class HGFriendsModule : FriendsModule, ISharedRegionModule, IFriendsModule, IFriendsSimConnector
50 {
51 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52
53 #region ISharedRegionModule
54 public override string Name
55 {
56 get { return "HGFriendsModule"; }
57 }
58
59 public override void AddRegion(Scene scene)
60 {
61 if (!m_Enabled)
62 return;
63
64 base.AddRegion(scene);
65 scene.RegisterModuleInterface<IFriendsSimConnector>(this);
66 }
67
68 #endregion
69
70 #region IFriendsSimConnector
71
72 /// <summary>
73 /// Notify the user that the friend's status changed
74 /// </summary>
75 /// <param name="userID">user to be notified</param>
76 /// <param name="friendID">friend whose status changed</param>
77 /// <param name="online">status</param>
78 /// <returns></returns>
79 public bool StatusNotify(UUID userID, UUID friendID, bool online)
80 {
81 return LocalStatusNotification(friendID, userID, online);
82 }
83
84 #endregion
85
86 protected override bool FetchFriendslist(IClientAPI client)
87 {
88 if (base.FetchFriendslist(client))
89 {
90 UUID agentID = client.AgentId;
91 // We need to preload the user management cache with the names
92 // of foreign friends, just like we do with SOPs' creators
93 foreach (FriendInfo finfo in m_Friends[agentID].Friends)
94 {
95 if (finfo.TheirFlags != -1)
96 {
97 UUID id;
98 if (!UUID.TryParse(finfo.Friend, out id))
99 {
100 string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
101 if (Util.ParseUniversalUserIdentifier(finfo.Friend, out id, out url, out first, out last, out tmp))
102 {
103 IUserManagement uMan = m_Scenes[0].RequestModuleInterface<IUserManagement>();
104 uMan.AddUser(id, url + ";" + first + " " + last);
105 }
106 }
107 }
108 }
109 return true;
110 }
111 return false;
112 }
113
114 public override bool SendFriendsOnlineIfNeeded(IClientAPI client)
115 {
116 if (base.SendFriendsOnlineIfNeeded(client))
117 {
118 UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(client.Scene.RegionInfo.ScopeID, client.AgentId);
119 if (account == null) // foreign
120 {
121 FriendInfo[] friends = GetFriends(client.AgentId);
122 foreach (FriendInfo f in friends)
123 {
124 client.SendChangeUserRights(new UUID(f.Friend), client.AgentId, f.TheirFlags);
125 }
126 }
127 }
128 return false;
129 }
130
131 protected override void GetOnlineFriends(UUID userID, List<string> friendList, /*collector*/ List<UUID> online)
132 {
133 // Let's single out the UUIs
134 List<string> localFriends = new List<string>();
135 List<string> foreignFriends = new List<string>();
136 string tmp = string.Empty;
137
138 foreach (string s in friendList)
139 {
140 UUID id;
141 if (UUID.TryParse(s, out id))
142 localFriends.Add(s);
143 else if (Util.ParseUniversalUserIdentifier(s, out id, out tmp, out tmp, out tmp, out tmp))
144 {
145 foreignFriends.Add(s);
146 // add it here too, who knows maybe the foreign friends happens to be on this grid
147 localFriends.Add(id.ToString());
148 }
149 }
150
151 // OK, see who's present on this grid
152 List<string> toBeRemoved = new List<string>();
153 PresenceInfo[] presence = PresenceService.GetAgents(localFriends.ToArray());
154 foreach (PresenceInfo pi in presence)
155 {
156 UUID presenceID;
157 if (UUID.TryParse(pi.UserID, out presenceID))
158 {
159 online.Add(presenceID);
160 foreach (string s in foreignFriends)
161 if (s.StartsWith(pi.UserID))
162 toBeRemoved.Add(s);
163 }
164 }
165
166 foreach (string s in toBeRemoved)
167 foreignFriends.Remove(s);
168
169 // OK, let's send this up the stack, and leave a closure here
170 // collecting online friends in other grids
171 Util.FireAndForget(delegate { CollectOnlineFriendsElsewhere(userID, foreignFriends); });
172
173 }
174
175 private void CollectOnlineFriendsElsewhere(UUID userID, List<string> foreignFriends)
176 {
177 // let's divide the friends on a per-domain basis
178 Dictionary<string, List<string>> friendsPerDomain = new Dictionary<string, List<string>>();
179 foreach (string friend in foreignFriends)
180 {
181 UUID friendID;
182 if (!UUID.TryParse(friend, out friendID))
183 {
184 // it's a foreign friend
185 string url = string.Empty, tmp = string.Empty;
186 if (Util.ParseUniversalUserIdentifier(friend, out friendID, out url, out tmp, out tmp, out tmp))
187 {
188 if (!friendsPerDomain.ContainsKey(url))
189 friendsPerDomain[url] = new List<string>();
190 friendsPerDomain[url].Add(friend);
191 }
192 }
193 }
194
195 // Now, call those worlds
196
197 foreach (KeyValuePair<string, List<string>> kvp in friendsPerDomain)
198 {
199 List<string> ids = new List<string>();
200 foreach (string f in kvp.Value)
201 ids.Add(f);
202 UserAgentServiceConnector uConn = new UserAgentServiceConnector(kvp.Key);
203 List<UUID> online = uConn.GetOnlineFriends(userID, ids);
204 // Finally send the notifications to the user
205 // this whole process may take a while, so let's check at every
206 // iteration that the user is still here
207 IClientAPI client = LocateClientObject(userID);
208 if (client != null)
209 client.SendAgentOnline(online.ToArray());
210 else
211 break;
212 }
213
214 }
215
216 protected override void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online)
217 {
218 // First, let's divide the friends on a per-domain basis
219 Dictionary<string, List<FriendInfo>> friendsPerDomain = new Dictionary<string, List<FriendInfo>>();
220 foreach (FriendInfo friend in friendList)
221 {
222 UUID friendID;
223 if (UUID.TryParse(friend.Friend, out friendID))
224 {
225 if (!friendsPerDomain.ContainsKey("local"))
226 friendsPerDomain["local"] = new List<FriendInfo>();
227 friendsPerDomain["local"].Add(friend);
228 }
229 else
230 {
231 // it's a foreign friend
232 string url = string.Empty, tmp = string.Empty;
233 if (Util.ParseUniversalUserIdentifier(friend.Friend, out friendID, out url, out tmp, out tmp, out tmp))
234 {
235 // Let's try our luck in the local sim. Who knows, maybe it's here
236 if (LocalStatusNotification(userID, friendID, online))
237 continue;
238
239 if (!friendsPerDomain.ContainsKey(url))
240 friendsPerDomain[url] = new List<FriendInfo>();
241 friendsPerDomain[url].Add(friend);
242 }
243 }
244 }
245
246 // For the local friends, just call the base method
247 // Let's do this first of all
248 if (friendsPerDomain.ContainsKey("local"))
249 base.StatusNotify(friendsPerDomain["local"], userID, online);
250
251 foreach (KeyValuePair<string, List<FriendInfo>> kvp in friendsPerDomain)
252 {
253 if (kvp.Key != "local")
254 {
255 // For the others, call the user agent service
256 List<string> ids = new List<string>();
257 foreach (FriendInfo f in kvp.Value)
258 ids.Add(f.Friend);
259 UserAgentServiceConnector uConn = new UserAgentServiceConnector(kvp.Key);
260 uConn.StatusNotification(ids, userID, online);
261 }
262 }
263 }
264
265 protected override bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last)
266 {
267 first = "Unknown"; last = "User";
268 if (base.GetAgentInfo(scopeID, fid, out agentID, out first, out last))
269 return true;
270
271 // fid is not a UUID...
272 string url = string.Empty, tmp = string.Empty;
273 if (Util.ParseUniversalUserIdentifier(fid, out agentID, out url, out first, out last, out tmp))
274 {
275 IUserManagement userMan = m_Scenes[0].RequestModuleInterface<IUserManagement>();
276 userMan.AddUser(agentID, url + ";" + first + " " + last);
277
278 try // our best
279 {
280 string[] parts = userMan.GetUserName(agentID).Split();
281 first = parts[0];
282 last = parts[1];
283 }
284 catch { }
285 return true;
286 }
287 return false;
288 }
289
290 protected override string GetFriendshipRequesterName(UUID agentID)
291 {
292 // For the time being we assume that HG friendship requests can only happen
293 // when avies are on the same region.
294 IClientAPI client = LocateClientObject(agentID);
295 if (client != null)
296 return client.FirstName + " " + client.LastName;
297 else
298 return base.GetFriendshipRequesterName(agentID);
299 }
300
301 protected override string FriendshipMessage(string friendID)
302 {
303 UUID id;
304 if (UUID.TryParse(friendID, out id))
305 return base.FriendshipMessage(friendID);
306
307 return "Please confirm this friendship you made while you were away.";
308 }
309
310 protected override FriendInfo GetFriend(FriendInfo[] friends, UUID friendID)
311 {
312 foreach (FriendInfo fi in friends)
313 {
314 if (fi.Friend.StartsWith(friendID.ToString()))
315 return fi;
316 }
317 return null;
318 }
319
320
321 protected override FriendInfo[] GetFriendsFromService(IClientAPI client)
322 {
323 UserAccount account1 = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, client.AgentId);
324 if (account1 != null)
325 return base.GetFriendsFromService(client);
326
327 FriendInfo[] finfos = new FriendInfo[0];
328 // Foreigner
329 AgentCircuitData agentClientCircuit = ((Scene)(client.Scene)).AuthenticateHandler.GetAgentCircuitData(client.CircuitCode);
330 if (agentClientCircuit != null)
331 {
332 string agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit);
333
334 finfos = FriendsService.GetFriends(agentUUI);
335 m_log.DebugFormat("[HGFRIENDS MODULE]: Fetched {0} local friends for visitor {1}", finfos.Length, agentUUI);
336 }
337 return finfos;
338 }
339
340 protected override bool StoreRights(UUID agentID, UUID friendID, int rights)
341 {
342 UserAccount account1 = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, agentID);
343 UserAccount account2 = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, friendID);
344 // Are they both local users?
345 if (account1 != null && account2 != null)
346 {
347 // local grid users
348 return base.StoreRights(agentID, friendID, rights);
349 }
350
351 if (account1 != null) // agent is local, friend is foreigner
352 {
353 FriendInfo[] finfos = GetFriends(agentID);
354 FriendInfo finfo = GetFriend(finfos, friendID);
355 if (finfo != null)
356 {
357 FriendsService.StoreFriend(agentID.ToString(), finfo.Friend, rights);
358 return true;
359 }
360 }
361
362 if (account2 != null) // agent is foreigner, friend is local
363 {
364 string agentUUI = GetUUI(friendID, agentID);
365 if (agentUUI != string.Empty)
366 {
367 FriendsService.StoreFriend(agentUUI, friendID.ToString(), rights);
368 return true;
369 }
370 }
371
372 return false;
373
374 }
375
376 protected override void StoreBackwards(UUID friendID, UUID agentID)
377 {
378 UserAccount account1 = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, agentID);
379 UserAccount account2 = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, friendID);
380 // Are they both local users?
381 if (account1 != null && account2 != null)
382 {
383 // local grid users
384 m_log.DebugFormat("[HGFRIENDS MODULE]: Users are both local");
385 base.StoreBackwards(friendID, agentID);
386 return;
387 }
388
389 // no provision for this temporary friendship state
390 //FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0);
391 }
392
393 protected override void StoreFriendships(UUID agentID, UUID friendID)
394 {
395 UserAccount agentAccount = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, agentID);
396 UserAccount friendAccount = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, friendID);
397 // Are they both local users?
398 if (agentAccount != null && friendAccount != null)
399 {
400 // local grid users
401 m_log.DebugFormat("[HGFRIENDS MODULE]: Users are both local");
402 base.StoreFriendships(agentID, friendID);
403 return;
404 }
405
406 // ok, at least one of them is foreigner, let's get their data
407 IClientAPI agentClient = LocateClientObject(agentID);
408 IClientAPI friendClient = LocateClientObject(friendID);
409 AgentCircuitData agentClientCircuit = null;
410 AgentCircuitData friendClientCircuit = null;
411 string agentUUI = string.Empty;
412 string friendUUI = string.Empty;
413 string agentFriendService = string.Empty;
414 string friendFriendService = string.Empty;
415
416 if (agentClient != null)
417 {
418 agentClientCircuit = ((Scene)(agentClient.Scene)).AuthenticateHandler.GetAgentCircuitData(agentClient.CircuitCode);
419 agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit);
420 agentFriendService = agentClientCircuit.ServiceURLs["FriendsServerURI"].ToString();
421 }
422 if (friendClient != null)
423 {
424 friendClientCircuit = ((Scene)(friendClient.Scene)).AuthenticateHandler.GetAgentCircuitData(friendClient.CircuitCode);
425 friendUUI = Util.ProduceUserUniversalIdentifier(friendClientCircuit);
426 friendFriendService = friendClientCircuit.ServiceURLs["FriendsServerURI"].ToString();
427 }
428
429 m_log.DebugFormat("[HGFRIENDS MODULE] HG Friendship! thisUUI={0}; friendUUI={1}; foreignThisFriendService={2}; foreignFriendFriendService={3}",
430 agentUUI, friendUUI, agentFriendService, friendFriendService);
431
432 // Generate a random 8-character hex number that will sign this friendship
433 string secret = UUID.Random().ToString().Substring(0, 8);
434
435 if (agentAccount != null) // agent is local, 'friend' is foreigner
436 {
437 // This may happen when the agent returned home, in which case the friend is not there
438 // We need to look for its information in the friends list itself
439 bool confirming = false;
440 if (friendUUI == string.Empty)
441 {
442 FriendInfo[] finfos = GetFriends(agentID);
443 foreach (FriendInfo finfo in finfos)
444 {
445 if (finfo.TheirFlags == -1)
446 {
447 if (finfo.Friend.StartsWith(friendID.ToString()))
448 {
449 friendUUI = finfo.Friend;
450 confirming = true;
451 }
452 }
453 }
454 }
455
456 // If it's confirming the friendship, we already have the full friendUUI with the secret
457 string theFriendUUID = confirming ? friendUUI : friendUUI + ";" + secret;
458
459 // store in the local friends service a reference to the foreign friend
460 FriendsService.StoreFriend(agentID.ToString(), theFriendUUID, 1);
461 // and also the converse
462 FriendsService.StoreFriend(theFriendUUID, agentID.ToString(), 1);
463
464 if (!confirming && friendClientCircuit != null)
465 {
466 // store in the foreign friends service a reference to the local agent
467 HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(friendFriendService, friendClientCircuit.SessionID, friendClientCircuit.ServiceSessionID);
468 friendsConn.NewFriendship(friendID, agentUUI + ";" + secret);
469 }
470 }
471 else if (friendAccount != null) // 'friend' is local, agent is foreigner
472 {
473 // store in the local friends service a reference to the foreign agent
474 FriendsService.StoreFriend(friendID.ToString(), agentUUI + ";" + secret, 1);
475 // and also the converse
476 FriendsService.StoreFriend(agentUUI + ";" + secret, friendID.ToString(), 1);
477
478 if (agentClientCircuit != null)
479 {
480 // store in the foreign friends service a reference to the local agent
481 HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(agentFriendService, agentClientCircuit.SessionID, agentClientCircuit.ServiceSessionID);
482 friendsConn.NewFriendship(agentID, friendUUI + ";" + secret);
483 }
484 }
485 else // They're both foreigners!
486 {
487 HGFriendsServicesConnector friendsConn;
488 if (agentClientCircuit != null)
489 {
490 friendsConn = new HGFriendsServicesConnector(agentFriendService, agentClientCircuit.SessionID, agentClientCircuit.ServiceSessionID);
491 friendsConn.NewFriendship(agentID, friendUUI + ";" + secret);
492 }
493 if (friendClientCircuit != null)
494 {
495 friendsConn = new HGFriendsServicesConnector(friendFriendService, friendClientCircuit.SessionID, friendClientCircuit.ServiceSessionID);
496 friendsConn.NewFriendship(friendID, agentUUI + ";" + secret);
497 }
498 }
499 // my brain hurts now
500 }
501
502 protected override bool DeleteFriendship(UUID agentID, UUID exfriendID)
503 {
504 UserAccount agentAccount = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, agentID);
505 UserAccount friendAccount = UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, exfriendID);
506 // Are they both local users?
507 if (agentAccount != null && friendAccount != null)
508 {
509 // local grid users
510 return base.DeleteFriendship(agentID, exfriendID);
511 }
512
513 // ok, at least one of them is foreigner, let's get their data
514 string agentUUI = string.Empty;
515 string friendUUI = string.Empty;
516
517 if (agentAccount != null) // agent is local, 'friend' is foreigner
518 {
519 // We need to look for its information in the friends list itself
520 FriendInfo[] finfos = GetFriends(agentID);
521 FriendInfo finfo = GetFriend(finfos, exfriendID);
522 if (finfo != null)
523 {
524 friendUUI = finfo.Friend;
525
526 // delete in the local friends service the reference to the foreign friend
527 FriendsService.Delete(agentID, friendUUI);
528 // and also the converse
529 FriendsService.Delete(friendUUI, agentID.ToString());
530
531 // notify the exfriend's service
532 Util.FireAndForget(delegate { Delete(exfriendID, agentID, friendUUI); });
533
534 m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentID, friendUUI);
535 return true;
536 }
537 }
538 else if (friendAccount != null) // agent is foreigner, 'friend' is local
539 {
540 agentUUI = GetUUI(exfriendID, agentID);
541
542 if (agentUUI != string.Empty)
543 {
544 // delete in the local friends service the reference to the foreign agent
545 FriendsService.Delete(exfriendID, agentUUI);
546 // and also the converse
547 FriendsService.Delete(agentUUI, exfriendID.ToString());
548
549 // notify the agent's service?
550 Util.FireAndForget(delegate { Delete(agentID, exfriendID, agentUUI); });
551
552 m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentUUI, exfriendID);
553 return true;
554 }
555 }
556 //else They're both foreigners! Can't handle this
557
558 return false;
559 }
560
561 private string GetUUI(UUID localUser, UUID foreignUser)
562 {
563 // Let's see if the user is here by any chance
564 FriendInfo[] finfos = GetFriends(localUser);
565 if (finfos != EMPTY_FRIENDS) // friend is here, cool
566 {
567 FriendInfo finfo = GetFriend(finfos, foreignUser);
568 if (finfo != null)
569 {
570 return finfo.Friend;
571 }
572 }
573 else // user is not currently on this sim, need to get from the service
574 {
575 finfos = FriendsService.GetFriends(localUser);
576 foreach (FriendInfo finfo in finfos)
577 {
578 if (finfo.Friend.StartsWith(foreignUser.ToString())) // found it!
579 {
580 return finfo.Friend;
581 }
582 }
583 }
584 return string.Empty;
585 }
586
587 private void Delete(UUID foreignUser, UUID localUser, string uui)
588 {
589 UUID id;
590 string url = string.Empty, secret = string.Empty, tmp = string.Empty;
591 if (Util.ParseUniversalUserIdentifier(uui, out id, out url, out tmp, out tmp, out secret))
592 {
593 m_log.DebugFormat("[HGFRIENDS MODULE]: Deleting friendship from {0}", url);
594 HGFriendsServicesConnector friendConn = new HGFriendsServicesConnector(url);
595 friendConn.DeleteFriendship(foreignUser, localUser, secret);
596 }
597 }
598 }
599}