aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Avatar/Friends
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/Friends')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs1338
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs285
2 files changed, 732 insertions, 891 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
index 086d4fe..cc324fe 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
@@ -28,7 +28,6 @@
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic; 30using System.Collections.Generic;
31using System.Net;
32using System.Reflection; 31using System.Reflection;
33using log4net; 32using log4net;
34using Nini.Config; 33using Nini.Config;
@@ -36,1119 +35,676 @@ using Nwc.XmlRpc;
36using OpenMetaverse; 35using OpenMetaverse;
37using OpenSim.Framework; 36using OpenSim.Framework;
38using OpenSim.Framework.Communications; 37using OpenSim.Framework.Communications;
39using OpenSim.Framework.Communications.Cache;
40using OpenSim.Region.Framework.Interfaces; 38using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
42using OpenSim.Services.Interfaces; 40using OpenSim.Services.Interfaces;
41using OpenSim.Services.Connectors.Friends;
42using OpenSim.Server.Base;
43using OpenSim.Framework.Servers.HttpServer;
44using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
45using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
43using GridRegion = OpenSim.Services.Interfaces.GridRegion; 46using GridRegion = OpenSim.Services.Interfaces.GridRegion;
44 47
45namespace OpenSim.Region.CoreModules.Avatar.Friends 48namespace OpenSim.Region.CoreModules.Avatar.Friends
46{ 49{
47 /* 50 public class FriendsModule : ISharedRegionModule, IFriendsModule
48 This module handles adding/removing friends, and the the presence
49 notification process for login/logoff of friends.
50
51 The presence notification works as follows:
52 - After the user initially connects to a region (so we now have a UDP
53 connection to work with), this module fetches the friends of user
54 (those are cached), their on-/offline status, and info about the
55 region they are in from the MessageServer.
56 - (*) It then informs the user about the on-/offline status of her friends.
57 - It then informs all online friends currently on this region-server about
58 user's new online status (this will save some network traffic, as local
59 messages don't have to be transferred inter-region, and it will be all
60 that has to be done in Standalone Mode).
61 - For the rest of the online friends (those not on this region-server),
62 this module uses the provided region-information to map users to
63 regions, and sends one notification to every region containing the
64 friends to inform on that server.
65 - The region-server will handle that in the following way:
66 - If it finds the friend, it informs her about the user being online.
67 - If it doesn't find the friend (maybe she TPed away in the meantime),
68 it stores that information.
69 - After it processed all friends, it returns the list of friends it
70 couldn't find.
71 - If this list isn't empty, the FriendsModule re-requests information
72 about those online friends that have been missed and starts at (*)
73 again until all friends have been found, or until it tried 3 times
74 (to prevent endless loops due to some uncaught error).
75
76 NOTE: Online/Offline notifications don't need to be sent on region change.
77
78 We implement two XMLRpc handlers here, handling all the inter-region things
79 we have to handle:
80 - On-/Offline-Notifications (bulk)
81 - Terminate Friendship messages (single)
82 */
83
84 public class FriendsModule : IRegionModule, IFriendsModule
85 { 51 {
86 private class Transaction 52 protected class UserFriendData
87 { 53 {
88 public UUID agentID; 54 public UUID PrincipalID;
89 public string agentName; 55 public FriendInfo[] Friends;
90 public uint count; 56 public int Refcount;
57 public UUID RegionID;
91 58
92 public Transaction(UUID agentID, string agentName) 59 public bool IsFriend(string friend)
93 { 60 {
94 this.agentID = agentID; 61 foreach (FriendInfo fi in Friends)
95 this.agentName = agentName; 62 {
96 this.count = 1; 63 if (fi.Friend == friend)
64 return true;
65 }
66
67 return false;
97 } 68 }
98 } 69 }
99 70
100 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 71 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
101 72
102 private Cache m_friendLists = new Cache(CacheFlags.AllowUpdate); 73 protected List<Scene> m_Scenes = new List<Scene>();
103 74
104 private Dictionary<UUID, ulong> m_rootAgents = new Dictionary<UUID, ulong>(); 75 protected IPresenceService m_PresenceService = null;
76 protected IFriendsService m_FriendsService = null;
77 protected FriendsSimConnector m_FriendsSimConnector;
105 78
106 private Dictionary<UUID, UUID> m_pendingCallingcardRequests = new Dictionary<UUID,UUID>(); 79 protected Dictionary<UUID, UserFriendData> m_Friends =
80 new Dictionary<UUID, UserFriendData>();
107 81
108 private Scene m_initialScene; // saves a lookup if we don't have a specific scene 82 protected List<UUID> m_NeedsListOfFriends = new List<UUID>();
109 private Dictionary<ulong, Scene> m_scenes = new Dictionary<ulong,Scene>();
110 private IMessageTransferModule m_TransferModule = null;
111 83
112 private IGridService m_gridServices = null; 84 protected IPresenceService PresenceService
113
114 #region IRegionModule Members
115
116 public void Initialise(Scene scene, IConfigSource config)
117 { 85 {
118 lock (m_scenes) 86 get
119 { 87 {
120 if (m_scenes.Count == 0) 88 if (m_PresenceService == null)
121 { 89 {
122 MainServer.Instance.AddXmlRPCHandler("presence_update_bulk", processPresenceUpdateBulk); 90 if (m_Scenes.Count > 0)
123 MainServer.Instance.AddXmlRPCHandler("terminate_friend", processTerminateFriend); 91 m_PresenceService = m_Scenes[0].RequestModuleInterface<IPresenceService>();
124 m_friendLists.DefaultTTL = new TimeSpan(1, 0, 0); // store entries for one hour max
125 m_initialScene = scene;
126 } 92 }
127 93
128 if (!m_scenes.ContainsKey(scene.RegionInfo.RegionHandle)) 94 return m_PresenceService;
129 m_scenes[scene.RegionInfo.RegionHandle] = scene;
130 } 95 }
131
132 scene.RegisterModuleInterface<IFriendsModule>(this);
133
134 scene.EventManager.OnNewClient += OnNewClient;
135 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
136 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
137 scene.EventManager.OnMakeChildAgent += MakeChildAgent;
138 scene.EventManager.OnClientClosed += ClientClosed;
139 } 96 }
140 97
141 public void PostInitialise() 98 protected IFriendsService FriendsService
142 { 99 {
143 if (m_scenes.Count > 0) 100 get
144 { 101 {
145 m_TransferModule = m_initialScene.RequestModuleInterface<IMessageTransferModule>(); 102 if (m_FriendsService == null)
146 m_gridServices = m_initialScene.GridService; 103 {
147 } 104 if (m_Scenes.Count > 0)
148 if (m_TransferModule == null) 105 m_FriendsService = m_Scenes[0].RequestModuleInterface<IFriendsService>();
149 m_log.Error("[FRIENDS]: Unable to find a message transfer module, friendship offers will not work"); 106 }
150 }
151
152 public void Close()
153 {
154 }
155 107
156 public string Name 108 return m_FriendsService;
157 { 109 }
158 get { return "FriendsModule"; }
159 } 110 }
160 111
161 public bool IsSharedModule 112 protected IGridService GridService
162 { 113 {
163 get { return true; } 114 get
115 {
116 return m_Scenes[0].GridService;
117 }
164 } 118 }
165 119
166 #endregion 120 public IScene Scene
167
168 #region IInterregionFriendsComms
169
170 public List<UUID> InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List<UUID> friends, bool online)
171 { 121 {
172 List<UUID> tpdAway = new List<UUID>(); 122 get
173
174 // destRegionHandle is a region on another server
175 uint x = 0, y = 0;
176 Utils.LongToUInts(destRegionHandle, out x, out y);
177 GridRegion info = m_gridServices.GetRegionByPosition(m_initialScene.RegionInfo.ScopeID, (int)x, (int)y);
178 if (info != null)
179 { 123 {
180 string httpServer = "http://" + info.ExternalEndPoint.Address + ":" + info.HttpPort + "/presence_update_bulk"; 124 if (m_Scenes.Count > 0)
181 125 return m_Scenes[0];
182 Hashtable reqParams = new Hashtable(); 126 else
183 reqParams["agentID"] = agentId.ToString(); 127 return null;
184 reqParams["agentOnline"] = online;
185 int count = 0;
186 foreach (UUID uuid in friends)
187 {
188 reqParams["friendID_" + count++] = uuid.ToString();
189 }
190 reqParams["friendCount"] = count;
191
192 IList parameters = new ArrayList();
193 parameters.Add(reqParams);
194 try
195 {
196 XmlRpcRequest request = new XmlRpcRequest("presence_update_bulk", parameters);
197 XmlRpcResponse response = request.Send(httpServer, 5000);
198 Hashtable respData = (Hashtable)response.Value;
199
200 count = (int)respData["friendCount"];
201 for (int i = 0; i < count; ++i)
202 {
203 UUID uuid;
204 if (UUID.TryParse((string)respData["friendID_" + i], out uuid)) tpdAway.Add(uuid);
205 }
206 }
207 catch (WebException e)
208 {
209 // Ignore connect failures, simulators come and go
210 //
211 if (!e.Message.Contains("ConnectFailure"))
212 {
213 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e);
214 }
215 }
216 catch (Exception e)
217 {
218 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e);
219 }
220 } 128 }
221 else m_log.WarnFormat("[OGS1 GRID SERVICES]: Couldn't find region {0}???", destRegionHandle);
222
223 return tpdAway;
224 } 129 }
225 130
226 public bool TriggerTerminateFriend(ulong destRegionHandle, UUID agentID, UUID exFriendID) 131 public void Initialise(IConfigSource config)
227 { 132 {
228 // destRegionHandle is a region on another server 133 IConfig friendsConfig = config.Configs["Friends"];
229 uint x = 0, y = 0; 134 if (friendsConfig != null)
230 Utils.LongToUInts(destRegionHandle, out x, out y);
231 GridRegion info = m_gridServices.GetRegionByPosition(m_initialScene.RegionInfo.ScopeID, (int)x, (int)y);
232 if (info == null)
233 { 135 {
234 m_log.WarnFormat("[OGS1 GRID SERVICES]: Couldn't find region {0}", destRegionHandle); 136 int mPort = friendsConfig.GetInt("Port", 0);
235 return false; // region not found???
236 }
237 137
238 string httpServer = "http://" + info.ExternalEndPoint.Address + ":" + info.HttpPort + "/presence_update_bulk"; 138 string connector = friendsConfig.GetString("Connector", String.Empty);
139 Object[] args = new Object[] { config };
239 140
240 Hashtable reqParams = new Hashtable(); 141 m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(connector, args);
241 reqParams["agentID"] = agentID.ToString(); 142 m_FriendsSimConnector = new FriendsSimConnector();
242 reqParams["friendID"] = exFriendID.ToString();
243 143
244 IList parameters = new ArrayList(); 144 // Instantiate the request handler
245 parameters.Add(reqParams); 145 IHttpServer server = MainServer.GetHttpServer((uint)mPort);
246 try 146 server.AddStreamHandler(new FriendsRequestHandler(this));
247 {
248 XmlRpcRequest request = new XmlRpcRequest("terminate_friend", parameters);
249 XmlRpcResponse response = request.Send(httpServer, 5000);
250 Hashtable respData = (Hashtable)response.Value;
251 147
252 return (bool)respData["success"];
253 } 148 }
254 catch (Exception e) 149
150 if (m_FriendsService == null)
255 { 151 {
256 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e); 152 m_log.Error("[FRIENDS]: No Connector defined in section Friends, or filed to load, cannot continue");
257 return false; 153 throw new Exception("Connector load error");
258 } 154 }
155
259 } 156 }
260 157
261 #endregion 158 public void PostInitialise()
159 {
160 }
262 161
263 #region Incoming XMLRPC messages 162 public void Close()
264 /// <summary>
265 /// Receive presence information changes about clients in other regions.
266 /// </summary>
267 /// <param name="req"></param>
268 /// <returns></returns>
269 public XmlRpcResponse processPresenceUpdateBulk(XmlRpcRequest req, IPEndPoint remoteClient)
270 { 163 {
271 Hashtable requestData = (Hashtable)req.Params[0]; 164 }
272 165
273 List<UUID> friendsNotHere = new List<UUID>(); 166 public void AddRegion(Scene scene)
167 {
168 m_Scenes.Add(scene);
169 scene.RegisterModuleInterface<IFriendsModule>(this);
274 170
275 // this is called with the expectation that all the friends in the request are on this region-server. 171 scene.EventManager.OnNewClient += OnNewClient;
276 // But as some time passed since we checked (on the other region-server, via the MessagingServer), 172 scene.EventManager.OnClientClosed += OnClientClosed;
277 // some of the friends might have teleported away. 173 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
278 // Actually, even now, between this line and the sending below, some people could TP away. So, 174 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
279 // we'll have to lock the m_rootAgents list for the duration to prevent/delay that. 175 scene.EventManager.OnClientLogin += OnClientLogin;
280 lock (m_rootAgents) 176 }
281 {
282 List<ScenePresence> friendsHere = new List<ScenePresence>();
283
284 try
285 {
286 UUID agentID = new UUID((string)requestData["agentID"]);
287 bool agentOnline = (bool)requestData["agentOnline"];
288 int count = (int)requestData["friendCount"];
289 for (int i = 0; i < count; ++i)
290 {
291 UUID uuid;
292 if (UUID.TryParse((string)requestData["friendID_" + i], out uuid))
293 {
294 if (m_rootAgents.ContainsKey(uuid)) friendsHere.Add(GetRootPresenceFromAgentID(uuid));
295 else friendsNotHere.Add(uuid);
296 }
297 }
298 177
299 // now send, as long as they are still here... 178 public void RegionLoaded(Scene scene)
300 UUID[] agentUUID = new UUID[] { agentID }; 179 {
301 if (agentOnline) 180 }
302 {
303 foreach (ScenePresence agent in friendsHere)
304 {
305 agent.ControllingClient.SendAgentOnline(agentUUID);
306 }
307 }
308 else
309 {
310 foreach (ScenePresence agent in friendsHere)
311 {
312 agent.ControllingClient.SendAgentOffline(agentUUID);
313 }
314 }
315 }
316 catch(Exception e)
317 {
318 m_log.Warn("[FRIENDS]: Got exception while parsing presence_update_bulk request:", e);
319 }
320 }
321 181
322 // no need to lock anymore; if TPs happen now, worst case is that we have an additional agent in this region, 182 public void RemoveRegion(Scene scene)
323 // which should be caught on the next iteration... 183 {
324 Hashtable result = new Hashtable(); 184 m_Scenes.Remove(scene);
325 int idx = 0; 185 }
326 foreach (UUID uuid in friendsNotHere)
327 {
328 result["friendID_" + idx++] = uuid.ToString();
329 }
330 result["friendCount"] = idx;
331 186
332 XmlRpcResponse response = new XmlRpcResponse(); 187 public string Name
333 response.Value = result; 188 {
189 get { return "FriendsModule"; }
190 }
334 191
335 return response; 192 public Type ReplaceableInterface
193 {
194 get { return null; }
336 } 195 }
337 196
338 public XmlRpcResponse processTerminateFriend(XmlRpcRequest req, IPEndPoint remoteClient) 197 public uint GetFriendPerms(UUID principalID, UUID friendID)
339 { 198 {
340 Hashtable requestData = (Hashtable)req.Params[0]; 199 if (!m_Friends.ContainsKey(principalID))
200 return 0;
341 201
342 bool success = false; 202 UserFriendData data = m_Friends[principalID];
343 203
344 UUID agentID; 204 foreach (FriendInfo fi in data.Friends)
345 UUID friendID;
346 if (requestData.ContainsKey("agentID") && UUID.TryParse((string)requestData["agentID"], out agentID) &&
347 requestData.ContainsKey("friendID") && UUID.TryParse((string)requestData["friendID"], out friendID))
348 { 205 {
349 // try to find it and if it is there, prevent it to vanish before we sent the message 206 if (fi.Friend == friendID.ToString())
350 lock (m_rootAgents) 207 return (uint)fi.TheirFlags;
351 {
352 if (m_rootAgents.ContainsKey(agentID))
353 {
354 m_log.DebugFormat("[FRIEND]: Sending terminate friend {0} to agent {1}", friendID, agentID);
355 GetRootPresenceFromAgentID(agentID).ControllingClient.SendTerminateFriend(friendID);
356 success = true;
357 }
358 }
359 } 208 }
360 209 return 0;
361 // return whether we were successful
362 Hashtable result = new Hashtable();
363 result["success"] = success;
364
365 XmlRpcResponse response = new XmlRpcResponse();
366 response.Value = result;
367 return response;
368 } 210 }
369 211
370 #endregion
371
372 #region Scene events
373
374 private void OnNewClient(IClientAPI client) 212 private void OnNewClient(IClientAPI client)
375 { 213 {
376 // All friends establishment protocol goes over instant message
377 // There's no way to send a message from the sim
378 // to a user to 'add a friend' without causing dialog box spam
379
380 // Subscribe to instant messages
381 client.OnInstantMessage += OnInstantMessage; 214 client.OnInstantMessage += OnInstantMessage;
382
383 // Friend list management
384 client.OnApproveFriendRequest += OnApproveFriendRequest; 215 client.OnApproveFriendRequest += OnApproveFriendRequest;
385 client.OnDenyFriendRequest += OnDenyFriendRequest; 216 client.OnDenyFriendRequest += OnDenyFriendRequest;
386 client.OnTerminateFriendship += OnTerminateFriendship; 217 client.OnTerminateFriendship += OnTerminateFriendship;
387 218
388 // ... calling card handling... 219 client.OnGrantUserRights += OnGrantUserRights;
389 client.OnOfferCallingCard += OnOfferCallingCard;
390 client.OnAcceptCallingCard += OnAcceptCallingCard;
391 client.OnDeclineCallingCard += OnDeclineCallingCard;
392 220
393 // we need this one exactly once per agent session (see comments in the handler below)
394 client.OnEconomyDataRequest += OnEconomyDataRequest;
395
396 // if it leaves, we want to know, too
397 client.OnLogout += OnLogout; 221 client.OnLogout += OnLogout;
398
399 client.OnGrantUserRights += GrantUserFriendRights;
400 client.OnTrackAgent += FindAgent;
401 client.OnFindAgent += FindAgent;
402 222
403 } 223 if (m_Friends.ContainsKey(client.AgentId))
404
405 private void ClientClosed(UUID AgentId, Scene scene)
406 {
407 // agent's client was closed. As we handle logout in OnLogout, this here has only to handle
408 // TPing away (root agent is closed) or TPing/crossing in a region far enough away (client
409 // agent is closed).
410 // NOTE: In general, this doesn't mean that the agent logged out, just that it isn't around
411 // in one of the regions here anymore.
412 lock (m_rootAgents)
413 { 224 {
414 if (m_rootAgents.ContainsKey(AgentId)) 225 m_Friends[client.AgentId].Refcount++;
415 { 226 return;
416 m_rootAgents.Remove(AgentId);
417 }
418 } 227 }
419 }
420 228
421 private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID) 229 UserFriendData newFriends = new UserFriendData();
422 { 230
423 lock (m_rootAgents) 231 newFriends.PrincipalID = client.AgentId;
424 { 232 newFriends.Friends = m_FriendsService.GetFriends(client.AgentId);
425 m_rootAgents[avatar.UUID] = avatar.RegionHandle; 233 newFriends.Refcount = 1;
426 // Claim User! my user! Mine mine mine! 234 newFriends.RegionID = UUID.Zero;
427 } 235
236 m_Friends.Add(client.AgentId, newFriends);
237
238 //StatusChange(client.AgentId, true);
428 } 239 }
429 240
430 private void MakeChildAgent(ScenePresence avatar) 241 private void OnClientClosed(UUID agentID, Scene scene)
431 { 242 {
432 lock (m_rootAgents) 243 if (m_Friends.ContainsKey(agentID))
433 { 244 {
434 if (m_rootAgents.ContainsKey(avatar.UUID)) 245 if (m_Friends[agentID].Refcount == 1)
435 { 246 m_Friends.Remove(agentID);
436 // only delete if the region matches. As this is a shared module, the avatar could be 247 else
437 // root agent in another region on this server. 248 m_Friends[agentID].Refcount--;
438 if (m_rootAgents[avatar.UUID] == avatar.RegionHandle)
439 {
440 m_rootAgents.Remove(avatar.UUID);
441// m_log.Debug("[FRIEND]: Removing " + avatar.Firstname + " " + avatar.Lastname + " as a root agent");
442 }
443 }
444 } 249 }
445 } 250 }
446 #endregion
447 251
448 private ScenePresence GetRootPresenceFromAgentID(UUID AgentID) 252 private void OnLogout(IClientAPI client)
449 { 253 {
450 ScenePresence returnAgent = null; 254 StatusChange(client.AgentId, false);
451 lock (m_scenes) 255 m_Friends.Remove(client.AgentId);
452 {
453 ScenePresence queryagent = null;
454 foreach (Scene scene in m_scenes.Values)
455 {
456 queryagent = scene.GetScenePresence(AgentID);
457 if (queryagent != null)
458 {
459 if (!queryagent.IsChildAgent)
460 {
461 returnAgent = queryagent;
462 break;
463 }
464 }
465 }
466 }
467 return returnAgent;
468 } 256 }
469 257
470 private ScenePresence GetAnyPresenceFromAgentID(UUID AgentID) 258 private void OnMakeRootAgent(ScenePresence sp)
471 { 259 {
472 ScenePresence returnAgent = null; 260 UUID agentID = sp.ControllingClient.AgentId;
473 lock (m_scenes) 261
262 if (m_Friends.ContainsKey(agentID))
474 { 263 {
475 ScenePresence queryagent = null; 264 if (m_Friends[agentID].RegionID == UUID.Zero && m_Friends[agentID].Friends == null)
476 foreach (Scene scene in m_scenes.Values)
477 { 265 {
478 queryagent = scene.GetScenePresence(AgentID); 266 m_Friends[agentID].Friends =
479 if (queryagent != null) 267 m_FriendsService.GetFriends(agentID);
480 {
481 returnAgent = queryagent;
482 break;
483 }
484 } 268 }
485 } 269 m_Friends[agentID].RegionID =
486 return returnAgent; 270 sp.ControllingClient.Scene.RegionInfo.RegionID;
487 }
488
489 public void OfferFriendship(UUID fromUserId, IClientAPI toUserClient, string offerMessage)
490 {
491 CachedUserInfo userInfo = m_initialScene.CommsManager.UserProfileCacheService.GetUserDetails(fromUserId);
492
493 if (userInfo != null)
494 {
495 GridInstantMessage msg = new GridInstantMessage(
496 toUserClient.Scene, fromUserId, userInfo.UserProfile.Name, toUserClient.AgentId,
497 (byte)InstantMessageDialog.FriendshipOffered, offerMessage, false, Vector3.Zero);
498
499 FriendshipOffered(msg);
500 }
501 else
502 {
503 m_log.ErrorFormat("[FRIENDS]: No user found for id {0} in OfferFriendship()", fromUserId);
504 } 271 }
505 } 272 }
506 273
507 #region FriendRequestHandling
508 274
509 private void OnInstantMessage(IClientAPI client, GridInstantMessage im) 275 private void OnMakeChildAgent(ScenePresence sp)
510 { 276 {
511 // Friend Requests go by Instant Message.. using the dialog param 277 UUID agentID = sp.ControllingClient.AgentId;
512 // https://wiki.secondlife.com/wiki/ImprovedInstantMessage
513 278
514 if (im.dialog == (byte)InstantMessageDialog.FriendshipOffered) // 38 279 if (m_Friends.ContainsKey(agentID))
515 {
516 // fromAgentName is the *destination* name (the friend we offer friendship to)
517 ScenePresence initiator = GetAnyPresenceFromAgentID(new UUID(im.fromAgentID));
518 im.fromAgentName = initiator != null ? initiator.Name : "(hippo)";
519
520 FriendshipOffered(im);
521 }
522 else if (im.dialog == (byte)InstantMessageDialog.FriendshipAccepted) // 39
523 {
524 FriendshipAccepted(client, im);
525 }
526 else if (im.dialog == (byte)InstantMessageDialog.FriendshipDeclined) // 40
527 { 280 {
528 FriendshipDeclined(client, im); 281 if (m_Friends[agentID].RegionID == sp.ControllingClient.Scene.RegionInfo.RegionID)
282 m_Friends[agentID].RegionID = UUID.Zero;
529 } 283 }
530 } 284 }
531
532 /// <summary>
533 /// Invoked when a user offers a friendship.
534 /// </summary>
535 ///
536 /// <param name="im"></param>
537 /// <param name="client"></param>
538 private void FriendshipOffered(GridInstantMessage im)
539 {
540 // this is triggered by the initiating agent:
541 // A local agent offers friendship to some possibly remote friend.
542 // A IM is triggered, processed here and sent to the friend (possibly in a remote region).
543
544 m_log.DebugFormat("[FRIEND]: Offer(38) - From: {0}, FromName: {1} To: {2}, Session: {3}, Message: {4}, Offline {5}",
545 im.fromAgentID, im.fromAgentName, im.toAgentID, im.imSessionID, im.message, im.offline);
546
547 // 1.20 protocol sends an UUID in the message field, instead of the friendship offer text.
548 // For interoperability, we have to clear that
549 if (Util.isUUID(im.message)) im.message = "";
550
551 // be sneeky and use the initiator-UUID as transactionID. This means we can be stateless.
552 // we have to look up the agent name on friendship-approval, though.
553 im.imSessionID = im.fromAgentID;
554 285
555 if (m_TransferModule != null) 286 private void OnClientLogin(IClientAPI client)
556 {
557 // Send it to whoever is the destination.
558 // If new friend is local, it will send an IM to the viewer.
559 // If new friend is remote, it will cause a OnGridInstantMessage on the remote server
560 m_TransferModule.SendInstantMessage(
561 im,
562 delegate(bool success)
563 {
564 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success);
565 }
566 );
567 }
568 }
569
570 /// <summary>
571 /// Invoked when a user accepts a friendship offer.
572 /// </summary>
573 /// <param name="im"></param>
574 /// <param name="client"></param>
575 private void FriendshipAccepted(IClientAPI client, GridInstantMessage im)
576 {
577 m_log.DebugFormat("[FRIEND]: 39 - from client {0}, agent {2} {3}, imsession {4} to {5}: {6} (dialog {7})",
578 client.AgentId, im.fromAgentID, im.fromAgentName, im.imSessionID, im.toAgentID, im.message, im.dialog);
579 }
580
581 /// <summary>
582 /// Invoked when a user declines a friendship offer.
583 /// </summary>
584 /// May not currently be used - see OnDenyFriendRequest() instead
585 /// <param name="im"></param>
586 /// <param name="client"></param>
587 private void FriendshipDeclined(IClientAPI client, GridInstantMessage im)
588 { 287 {
589 UUID fromAgentID = new UUID(im.fromAgentID); 288 UUID agentID = client.AgentId;
590 UUID toAgentID = new UUID(im.toAgentID); 289
591 290 // Inform the friends that this user is online
592 // declining the friendship offer causes a type 40 IM being sent to the (possibly remote) initiator 291 StatusChange(agentID, true);
593 // toAgentID is initiator, fromAgentID declined friendship
594 m_log.DebugFormat("[FRIEND]: 40 - from client {0}, agent {1} {2}, imsession {3} to {4}: {5} (dialog {6})",
595 client != null ? client.AgentId.ToString() : "<null>",
596 fromAgentID, im.fromAgentName, im.imSessionID, im.toAgentID, im.message, im.dialog);
597
598 // Send the decline to whoever is the destination.
599 GridInstantMessage msg
600 = new GridInstantMessage(
601 client.Scene, fromAgentID, client.Name, toAgentID,
602 im.dialog, im.message, im.offline != 0, im.Position);
603 292
604 // If new friend is local, it will send an IM to the viewer. 293 // Register that we need to send the list of online friends to this user
605 // If new friend is remote, it will cause a OnGridInstantMessage on the remote server 294 lock (m_NeedsListOfFriends)
606 m_TransferModule.SendInstantMessage(msg, 295 if (!m_NeedsListOfFriends.Contains(agentID))
607 delegate(bool success) { 296 {
608 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success); 297 m_NeedsListOfFriends.Add(agentID);
609 } 298 }
610 );
611 } 299 }
612 300
613 private void OnGridInstantMessage(GridInstantMessage msg) 301 public void SendFriendsOnlineIfNeeded(IClientAPI client)
614 { 302 {
615 // This event won't be raised unless we have that agent, 303 UUID agentID = client.AgentId;
616 // so we can depend on the above not trying to send 304 if (m_NeedsListOfFriends.Contains(agentID))
617 // via grid again
618 //m_log.DebugFormat("[FRIEND]: Got GridIM from {0}, to {1}, imSession {2}, message {3}, dialog {4}",
619 // msg.fromAgentID, msg.toAgentID, msg.imSessionID, msg.message, msg.dialog);
620
621 if (msg.dialog == (byte)InstantMessageDialog.FriendshipOffered ||
622 msg.dialog == (byte)InstantMessageDialog.FriendshipAccepted ||
623 msg.dialog == (byte)InstantMessageDialog.FriendshipDeclined)
624 { 305 {
625 // this should succeed as we *know* the root agent is here. 306 if (!m_Friends.ContainsKey(agentID))
626 m_TransferModule.SendInstantMessage(msg, 307 {
627 delegate(bool success) { 308 m_log.DebugFormat("[FRIENDS MODULE]: agent {0} not found in local cache", agentID);
628 //m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success); 309 return;
629 } 310 }
630 );
631 }
632 311
633 if (msg.dialog == (byte)InstantMessageDialog.FriendshipAccepted) 312 client = LocateClientObject(agentID);
634 { 313 if (client == null)
635 // for accept friendship, we have to do a bit more 314 {
636 ApproveFriendship(new UUID(msg.fromAgentID), new UUID(msg.toAgentID), msg.fromAgentName); 315 m_log.DebugFormat("[FRIENDS MODULE]: agent's client {0} not found in local scene", agentID);
316 return;
317 }
318
319 List<UUID> online = GetOnlineFriends(agentID);
320
321 if (online.Count > 0)
322 {
323 m_log.DebugFormat("[FRIENDS MODULE]: User {0} in region {1} has {2} friends online", client.AgentId, client.Scene.RegionInfo.RegionName, online.Count);
324 client.SendAgentOnline(online.ToArray());
325 }
326
327 lock (m_NeedsListOfFriends)
328 m_NeedsListOfFriends.Remove(agentID);
637 } 329 }
638 } 330 }
639 331
640 private void ApproveFriendship(UUID fromAgentID, UUID toAgentID, string fromName) 332 List<UUID> GetOnlineFriends(UUID userID)
641 { 333 {
642 m_log.DebugFormat("[FRIEND]: Approve friendship from {0} (ID: {1}) to {2}", 334 List<string> friendList = new List<string>();
643 fromAgentID, fromName, toAgentID);
644 335
645 // a new friend was added in the initiator's and friend's data, so the cache entries are wrong now. 336 foreach (FriendInfo fi in m_Friends[userID].Friends)
646 lock (m_friendLists)
647 { 337 {
648 m_friendLists.Invalidate(fromAgentID.ToString()); 338 if ((fi.TheirFlags & 1) != 0)
649 m_friendLists.Invalidate(toAgentID.ToString()); 339 friendList.Add(fi.Friend);
650 } 340 }
651 341
652 // now send presence update and add a calling card for the new friend 342 PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray());
653 343
654 ScenePresence initiator = GetAnyPresenceFromAgentID(toAgentID); 344 List<UUID> online = new List<UUID>();
655 if (initiator == null)
656 {
657 // quite wrong. Shouldn't happen.
658 m_log.WarnFormat("[FRIEND]: Coudn't find initiator of friend request {0}", toAgentID);
659 return;
660 }
661 345
662 m_log.DebugFormat("[FRIEND]: Tell {0} that {1} is online", 346 foreach (PresenceInfo pi in presence)
663 initiator.Name, fromName);
664 // tell initiator that friend is online
665 initiator.ControllingClient.SendAgentOnline(new UUID[] { fromAgentID });
666
667 // find the folder for the friend...
668 //InventoryFolderImpl folder =
669 // initiator.Scene.CommsManager.UserProfileCacheService.GetUserDetails(toAgentID).FindFolderForType((int)InventoryType.CallingCard);
670 IInventoryService invService = initiator.Scene.InventoryService;
671 InventoryFolderBase folder = invService.GetFolderForType(toAgentID, AssetType.CallingCard);
672 if (folder != null)
673 { 347 {
674 // ... and add the calling card 348 if (pi.Online)
675 CreateCallingCard(initiator.ControllingClient, fromAgentID, folder.ID, fromName); 349 {
350 online.Add(new UUID(pi.UserID));
351 //m_log.DebugFormat("[XXX] {0} friend online {1}", userID, pi.UserID);
352 }
676 } 353 }
354
355 return online;
677 } 356 }
678 357
679 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders) 358 //
359 // Find the client for a ID
360 //
361 public IClientAPI LocateClientObject(UUID agentID)
680 { 362 {
681 m_log.DebugFormat("[FRIEND]: Got approve friendship from {0} {1}, agentID {2}, tid {3}", 363 Scene scene = GetClientScene(agentID);
682 client.Name, client.AgentId, agentID, friendID); 364 if(scene == null)
365 return null;
683 366
684 // store the new friend persistently for both avatars 367 ScenePresence presence = scene.GetScenePresence(agentID);
685 m_initialScene.StoreAddFriendship(friendID, agentID, (uint) FriendRights.CanSeeOnline); 368 if(presence == null)
369 return null;
686 370
687 // The cache entries aren't valid anymore either, as we just added a friend to both sides. 371 return presence.ControllingClient;
688 lock (m_friendLists) 372 }
689 {
690 m_friendLists.Invalidate(agentID.ToString());
691 m_friendLists.Invalidate(friendID.ToString());
692 }
693
694 // if it's a local friend, we don't have to do the lookup
695 ScenePresence friendPresence = GetAnyPresenceFromAgentID(friendID);
696 373
697 if (friendPresence != null) 374 //
375 // Find the scene for an agent
376 //
377 private Scene GetClientScene(UUID agentId)
378 {
379 lock (m_Scenes)
698 { 380 {
699 m_log.Debug("[FRIEND]: Local agent detected."); 381 foreach (Scene scene in m_Scenes)
700 382 {
701 // create calling card 383 ScenePresence presence = scene.GetScenePresence(agentId);
702 CreateCallingCard(client, friendID, callingCardFolders[0], friendPresence.Name); 384 if (presence != null)
703 385 {
704 // local message means OnGridInstantMessage won't be triggered, so do the work here. 386 if (!presence.IsChildAgent)
705 friendPresence.ControllingClient.SendInstantMessage( 387 return scene;
706 new GridInstantMessage(client.Scene, agentID, 388 }
707 client.Name, friendID, 389 }
708 (byte)InstantMessageDialog.FriendshipAccepted,
709 agentID.ToString(), false, Vector3.Zero));
710 ApproveFriendship(agentID, friendID, client.Name);
711 } 390 }
712 else 391 return null;
713 { 392 }
714 m_log.Debug("[FRIEND]: Remote agent detected.");
715
716 // fetch the friend's name for the calling card.
717 CachedUserInfo info = m_initialScene.CommsManager.UserProfileCacheService.GetUserDetails(friendID);
718
719 // create calling card
720 CreateCallingCard(client, friendID, callingCardFolders[0],
721 info.UserProfile.FirstName + " " + info.UserProfile.SurName);
722 393
723 // Compose (remote) response to friend. 394 /// <summary>
724 GridInstantMessage msg = new GridInstantMessage(client.Scene, agentID, client.Name, friendID, 395 /// Caller beware! Call this only for root agents.
725 (byte)InstantMessageDialog.FriendshipAccepted, 396 /// </summary>
726 agentID.ToString(), false, Vector3.Zero); 397 /// <param name="agentID"></param>
727 if (m_TransferModule != null) 398 /// <param name="online"></param>
399 private void StatusChange(UUID agentID, bool online)
400 {
401 if (m_Friends.ContainsKey(agentID))
402 {
403 List<FriendInfo> friendList = new List<FriendInfo>();
404 foreach (FriendInfo fi in m_Friends[agentID].Friends)
728 { 405 {
729 m_TransferModule.SendInstantMessage(msg, 406 if ((fi.MyFlags & 1) != 0)
730 delegate(bool success) { 407 friendList.Add(fi);
731 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success); 408 }
732 } 409 foreach (FriendInfo fi in friendList)
733 ); 410 {
411 // Notify about this user status
412 StatusNotify(fi, agentID, online);
734 } 413 }
735 } 414 }
736
737 // tell client that new friend is online
738 client.SendAgentOnline(new UUID[] { friendID });
739 } 415 }
740 416
741 private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders) 417 private void StatusNotify(FriendInfo friend, UUID userID, bool online)
742 { 418 {
743 m_log.DebugFormat("[FRIEND]: Got deny friendship from {0} {1}, agentID {2}, tid {3}", 419 UUID friendID = UUID.Zero;
744 client.Name, client.AgentId, agentID, friendID); 420
745 421 if (UUID.TryParse(friend.Friend, out friendID))
746 // Compose response to other agent.
747 GridInstantMessage msg = new GridInstantMessage(client.Scene, agentID, client.Name, friendID,
748 (byte)InstantMessageDialog.FriendshipDeclined,
749 agentID.ToString(), false, Vector3.Zero);
750 // send decline to initiator
751 if (m_TransferModule != null)
752 { 422 {
753 m_TransferModule.SendInstantMessage(msg, 423 // Try local
754 delegate(bool success) { 424 if (LocalStatusNotification(userID, friendID, online))
755 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success); 425 return;
756 } 426
757 ); 427 // The friend is not here [as root]. Let's forward.
428 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
429 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
430 if (friendSession != null)
431 {
432 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
433 m_FriendsSimConnector.StatusNotify(region, userID, friendID, online);
434 }
435
436 // Friend is not online. Ignore.
758 } 437 }
759 } 438 }
760 439
761 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID) 440 private void OnInstantMessage(IClientAPI client, GridInstantMessage im)
762 { 441 {
763 // client.AgentId == agentID! 442 if (im.dialog == (byte)OpenMetaverse.InstantMessageDialog.FriendshipOffered)
443 {
444 // we got a friendship offer
445 UUID principalID = new UUID(im.fromAgentID);
446 UUID friendID = new UUID(im.toAgentID);
764 447
765 // this removes the friends from the stored friendlists. After the next login, they will be gone... 448 m_log.DebugFormat("[FRIENDS]: {0} offered friendship to {1}", principalID, friendID);
766 m_initialScene.StoreRemoveFriendship(agentID, exfriendID);
767 449
768 // ... now tell the two involved clients that they aren't friends anymore. 450 // This user wants to be friends with the other user.
451 // Let's add both relations to the DB, but one of them is inactive (-1)
452 FriendsService.StoreFriend(friendID, principalID.ToString(), 0);
769 453
770 // I don't know why we have to tell <agent>, as this was caused by her, but that's how it works in SL... 454 // Now let's ask the other user to be friends with this user
771 client.SendTerminateFriend(exfriendID); 455 ForwardFriendshipOffer(principalID, friendID, im);
772
773 // now send the friend, if online
774 ScenePresence presence = GetAnyPresenceFromAgentID(exfriendID);
775 if (presence != null)
776 {
777 m_log.DebugFormat("[FRIEND]: Sending terminate friend {0} to agent {1}", agentID, exfriendID);
778 presence.ControllingClient.SendTerminateFriend(agentID);
779 } 456 }
780 else 457 }
781 {
782 // retry 3 times, in case the agent TPed from the last known region...
783 for (int retry = 0; retry < 3; ++retry)
784 {
785 // wasn't sent, so ex-friend wasn't around on this region-server. Fetch info and try to send
786 UserAgentData data = m_initialScene.CommsManager.UserService.GetAgentByUUID(exfriendID);
787
788 if (null == data)
789 break;
790
791 if (!data.AgentOnline)
792 {
793 m_log.DebugFormat("[FRIEND]: {0} is offline, so not sending TerminateFriend", exfriendID);
794 break; // if ex-friend isn't online, we don't need to send
795 }
796 458
797 m_log.DebugFormat("[FRIEND]: Sending remote terminate friend {0} to agent {1}@{2}", 459 private void ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im)
798 agentID, exfriendID, data.Handle); 460 {
461 // !!!!!!!! This is a hack so that we don't have to keep state (transactionID/imSessionID)
462 // We stick this agent's ID as imSession, so that it's directly available on the receiving end
463 im.imSessionID = im.fromAgentID;
799 464
800 // try to send to foreign region, retry if it fails (friend TPed away, for example) 465 // Try the local sim
801 if (TriggerTerminateFriend(data.Handle, exfriendID, agentID)) break; 466 if (LocalFriendshipOffered(friendID, im))
802 } 467 return;
803 }
804 468
805 // clean up cache: FriendList is wrong now... 469 // The prospective friend is not here [as root]. Let's forward.
806 lock (m_friendLists) 470 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
471 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
472 if (friendSession != null)
807 { 473 {
808 m_friendLists.Invalidate(agentID.ToString()); 474 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
809 m_friendLists.Invalidate(exfriendID.ToString()); 475 m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message);
810 } 476 }
477
478 // If the prospective friend is not online, he'll get the message upon login.
811 } 479 }
812 480
813 #endregion 481 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders)
482 {
483 FriendsService.StoreFriend(agentID, friendID.ToString(), 1);
484 FriendsService.StoreFriend(friendID, agentID.ToString(), 1);
485 // update the local cache
486 m_Friends[agentID].Friends = FriendsService.GetFriends(agentID);
814 487
815 #region CallingCards 488 m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", agentID, friendID);
816 489
817 private void OnOfferCallingCard(IClientAPI client, UUID destID, UUID transactionID) 490 //
818 { 491 // Notify the friend
819 m_log.DebugFormat("[CALLING CARD]: got offer from {0} for {1}, transaction {2}", 492 //
820 client.AgentId, destID, transactionID); 493
821 // This might be slightly wrong. On a multi-region server, we might get the child-agent instead of the root-agent 494 // Try Local
822 // (or the root instead of the child) 495 if (LocalFriendshipApproved(agentID, client.Name, friendID))
823 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
824 if (destAgent == null)
825 { 496 {
826 client.SendAlertMessage("The person you have offered a card to can't be found anymore."); 497 client.SendAgentOnline(new UUID[] { friendID });
827 return; 498 return;
828 } 499 }
829 500
830 lock (m_pendingCallingcardRequests) 501 // The friend is not here
502 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
503 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
504 if (friendSession != null)
831 { 505 {
832 m_pendingCallingcardRequests[transactionID] = client.AgentId; 506 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
507 m_FriendsSimConnector.FriendshipApproved(region, agentID, client.Name, friendID);
508 client.SendAgentOnline(new UUID[] { friendID });
833 } 509 }
834 // inform the destination agent about the offer
835 destAgent.ControllingClient.SendOfferCallingCard(client.AgentId, transactionID);
836 }
837 510
838 private void CreateCallingCard(IClientAPI client, UUID creator, UUID folder, string name)
839 {
840 InventoryItemBase item = new InventoryItemBase();
841 item.AssetID = UUID.Zero;
842 item.AssetType = (int)AssetType.CallingCard;
843 item.BasePermissions = (uint)PermissionMask.Copy;
844 item.CreationDate = Util.UnixTimeSinceEpoch();
845 item.CreatorId = creator.ToString();
846 item.CurrentPermissions = item.BasePermissions;
847 item.Description = "";
848 item.EveryOnePermissions = (uint)PermissionMask.None;
849 item.Flags = 0;
850 item.Folder = folder;
851 item.GroupID = UUID.Zero;
852 item.GroupOwned = false;
853 item.ID = UUID.Random();
854 item.InvType = (int)InventoryType.CallingCard;
855 item.Name = name;
856 item.NextPermissions = item.EveryOnePermissions;
857 item.Owner = client.AgentId;
858 item.SalePrice = 10;
859 item.SaleType = (byte)SaleType.Not;
860 ((Scene)client.Scene).AddInventoryItem(client, item);
861 } 511 }
862 512
863 private void OnAcceptCallingCard(IClientAPI client, UUID transactionID, UUID folderID) 513 private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders)
864 { 514 {
865 m_log.DebugFormat("[CALLING CARD]: User {0} ({1} {2}) accepted tid {3}, folder {4}", 515 m_log.DebugFormat("[FRIENDS]: {0} denied friendship to {1}", agentID, friendID);
866 client.AgentId,
867 client.FirstName, client.LastName,
868 transactionID, folderID);
869 UUID destID;
870 lock (m_pendingCallingcardRequests)
871 {
872 if (!m_pendingCallingcardRequests.TryGetValue(transactionID, out destID))
873 {
874 m_log.WarnFormat("[CALLING CARD]: Got a AcceptCallingCard from {0} without an offer before.",
875 client.Name);
876 return;
877 }
878 // else found pending calling card request with that transaction.
879 m_pendingCallingcardRequests.Remove(transactionID);
880 }
881 516
517 FriendsService.Delete(agentID, friendID.ToString());
518 FriendsService.Delete(friendID, agentID.ToString());
882 519
883 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID); 520 //
884 // inform sender of the card that destination declined the offer 521 // Notify the friend
885 if (destAgent != null) destAgent.ControllingClient.SendAcceptCallingCard(transactionID); 522 //
886 523
887 // put a calling card into the inventory of receiver 524 // Try local
888 CreateCallingCard(client, destID, folderID, destAgent.Name); 525 if (LocalFriendshipDenied(agentID, client.Name, friendID))
889 } 526 return;
890 527
891 private void OnDeclineCallingCard(IClientAPI client, UUID transactionID) 528 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
892 { 529 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
893 m_log.DebugFormat("[CALLING CARD]: User {0} (ID:{1}) declined card, tid {2}", 530 if (friendSession != null)
894 client.Name, client.AgentId, transactionID);
895 UUID destID;
896 lock (m_pendingCallingcardRequests)
897 { 531 {
898 if (!m_pendingCallingcardRequests.TryGetValue(transactionID, out destID)) 532 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
899 { 533 m_FriendsSimConnector.FriendshipDenied(region, agentID, client.Name, friendID);
900 m_log.WarnFormat("[CALLING CARD]: Got a AcceptCallingCard from {0} without an offer before.",
901 client.Name);
902 return;
903 }
904 // else found pending calling card request with that transaction.
905 m_pendingCallingcardRequests.Remove(transactionID);
906 } 534 }
907
908 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
909 // inform sender of the card that destination declined the offer
910 if (destAgent != null) destAgent.ControllingClient.SendDeclineCallingCard(transactionID);
911 } 535 }
912 536
913 /// <summary> 537 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID)
914 /// Send presence information about a client to other clients in both this region and others.
915 /// </summary>
916 /// <param name="client"></param>
917 /// <param name="friendList"></param>
918 /// <param name="iAmOnline"></param>
919 private void SendPresenceState(IClientAPI client, List<FriendListItem> friendList, bool iAmOnline)
920 { 538 {
921 //m_log.DebugFormat("[FRIEND]: {0} logged {1}; sending presence updates", client.Name, iAmOnline ? "in" : "out"); 539 FriendsService.Delete(agentID, exfriendID.ToString());
540 FriendsService.Delete(exfriendID, agentID.ToString());
922 541
923 if (friendList == null || friendList.Count == 0) 542 // Update local cache
924 { 543 m_Friends[agentID].Friends = FriendsService.GetFriends(agentID);
925 //m_log.DebugFormat("[FRIEND]: {0} doesn't have friends.", client.Name);
926 return; // nothing we can do if she doesn't have friends...
927 }
928 544
929 // collect sets of friendIDs; to send to (online and offline), and to receive from 545 client.SendTerminateFriend(exfriendID);
930 // TODO: If we ever switch to .NET >= 3, replace those Lists with HashSets.
931 // I can't believe that we have Dictionaries, but no Sets, considering Java introduced them years ago...
932 List<UUID> friendIDsToSendTo = new List<UUID>();
933 List<UUID> candidateFriendIDsToReceive = new List<UUID>();
934
935 foreach (FriendListItem item in friendList)
936 {
937 if (((item.FriendListOwnerPerms | item.FriendPerms) & (uint)FriendRights.CanSeeOnline) != 0)
938 {
939 // friend is allowed to see my presence => add
940 if ((item.FriendListOwnerPerms & (uint)FriendRights.CanSeeOnline) != 0)
941 friendIDsToSendTo.Add(item.Friend);
942 546
943 if ((item.FriendPerms & (uint)FriendRights.CanSeeOnline) != 0) 547 //
944 candidateFriendIDsToReceive.Add(item.Friend); 548 // Notify the friend
945 } 549 //
946 }
947 550
948 // we now have a list of "interesting" friends (which we have to find out on-/offline state for), 551 // Try local
949 // friends we want to send our online state to (if *they* are online, too), and 552 if (LocalFriendshipTerminated(exfriendID))
950 // friends we want to receive online state for (currently unknown whether online or not) 553 return;
951 554
952 // as this processing might take some time and friends might TP away, we try up to three times to 555 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() });
953 // reach them. Most of the time, we *will* reach them, and this loop won't loop 556 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
954 int retry = 0; 557 if (friendSession != null)
955 do
956 { 558 {
957 // build a list of friends to look up region-information and on-/offline-state for 559 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
958 List<UUID> friendIDsToLookup = new List<UUID>(friendIDsToSendTo); 560 m_FriendsSimConnector.FriendshipTerminated(region, agentID, exfriendID);
959 foreach (UUID uuid in candidateFriendIDsToReceive) 561 }
960 { 562 }
961 if (!friendIDsToLookup.Contains(uuid)) friendIDsToLookup.Add(uuid);
962 }
963 563
964 m_log.DebugFormat( 564 private void OnGrantUserRights(IClientAPI remoteClient, UUID requester, UUID target, int rights)
965 "[FRIEND]: {0} to lookup, {1} to send to, {2} candidates to receive from for agent {3}", 565 {
966 friendIDsToLookup.Count, friendIDsToSendTo.Count, candidateFriendIDsToReceive.Count, client.Name); 566 if (!m_Friends.ContainsKey(remoteClient.AgentId))
567 return;
967 568
968 // we have to fetch FriendRegionInfos, as the (cached) FriendListItems don't 569 UserFriendData fd = m_Friends[remoteClient.AgentId];
969 // necessarily contain the correct online state...
970 Dictionary<UUID, FriendRegionInfo> friendRegions = m_initialScene.GetFriendRegionInfos(friendIDsToLookup);
971 m_log.DebugFormat(
972 "[FRIEND]: Found {0} regionInfos for {1} friends of {2}",
973 friendRegions.Count, friendIDsToLookup.Count, client.Name);
974 570
975 // argument for SendAgentOn/Offline; we shouldn't generate that repeatedly within loops. 571 FriendsService.StoreFriend(requester, target.ToString(), rights);
976 UUID[] agentArr = new UUID[] { client.AgentId };
977 572
978 // first, send to friend presence state to me, if I'm online... 573 foreach (FriendInfo fi in fd.Friends)
979 if (iAmOnline) 574 {
575 if (fi.Friend == target.ToString())
980 { 576 {
981 List<UUID> friendIDsToReceive = new List<UUID>(); 577 IClientAPI friendClient = LocateClientObject(target);
982
983 for (int i = candidateFriendIDsToReceive.Count - 1; i >= 0; --i)
984 {
985 UUID uuid = candidateFriendIDsToReceive[i];
986 FriendRegionInfo info;
987 if (friendRegions.TryGetValue(uuid, out info) && info != null && info.isOnline)
988 {
989 friendIDsToReceive.Add(uuid);
990 }
991 }
992
993 m_log.DebugFormat(
994 "[FRIEND]: Sending {0} online friends to {1}", friendIDsToReceive.Count, client.Name);
995
996 if (friendIDsToReceive.Count > 0)
997 client.SendAgentOnline(friendIDsToReceive.ToArray());
998
999 // clear them for a possible second iteration; we don't have to repeat this
1000 candidateFriendIDsToReceive.Clear();
1001 }
1002 578
1003 // now, send my presence state to my friends 579 int delta = rights ^ fi.MyFlags;
1004 for (int i = friendIDsToSendTo.Count - 1; i >= 0; --i) 580 if ((delta & ~1) != 0)
1005 {
1006 UUID uuid = friendIDsToSendTo[i];
1007 FriendRegionInfo info;
1008 if (friendRegions.TryGetValue(uuid, out info) && info != null && info.isOnline)
1009 { 581 {
1010 // any client is good enough, root or child... 582 remoteClient.SendChangeUserRights(remoteClient.AgentId, target, rights & ~1);
1011 ScenePresence agent = GetAnyPresenceFromAgentID(uuid); 583 //
1012 if (agent != null) 584 // Notify the friend
1013 { 585 //
1014 //m_log.DebugFormat("[FRIEND]: Found local agent {0}", agent.Name);
1015
1016 // friend is online and on this server...
1017 if (iAmOnline) agent.ControllingClient.SendAgentOnline(agentArr);
1018 else agent.ControllingClient.SendAgentOffline(agentArr);
1019 586
1020 // done, remove it 587 if (friendClient != null)
1021 friendIDsToSendTo.RemoveAt(i); 588 {
589 friendClient.SendChangeUserRights(target, remoteClient.AgentId, rights & ~1);
1022 } 590 }
1023 } 591 }
1024 else 592 if ((delta & 1) != 0)
1025 {
1026 //m_log.DebugFormat("[FRIEND]: Friend {0} ({1}) is offline; not sending.", uuid, i);
1027
1028 // friend is offline => no need to try sending
1029 friendIDsToSendTo.RemoveAt(i);
1030 }
1031 }
1032
1033 m_log.DebugFormat("[FRIEND]: Have {0} friends to contact via inter-region comms.", friendIDsToSendTo.Count);
1034
1035 // we now have all the friends left that are online (we think), but not on this region-server
1036 if (friendIDsToSendTo.Count > 0)
1037 {
1038 // sort them into regions
1039 Dictionary<ulong, List<UUID>> friendsInRegion = new Dictionary<ulong,List<UUID>>();
1040 foreach (UUID uuid in friendIDsToSendTo)
1041 { 593 {
1042 ulong handle = friendRegions[uuid].regionHandle; // this can't fail as we filtered above already 594 if (friendClient != null)
1043 List<UUID> friends;
1044 if (!friendsInRegion.TryGetValue(handle, out friends))
1045 { 595 {
1046 friends = new List<UUID>(); 596 if ((rights & 1) != 0)
1047 friendsInRegion[handle] = friends; 597 friendClient.SendAgentOnline(new UUID[] { new UUID(remoteClient.AgentId) } );
598 else
599 friendClient.SendAgentOffline(new UUID[] { new UUID(remoteClient.AgentId) } );
1048 } 600 }
1049 friends.Add(uuid);
1050 }
1051 m_log.DebugFormat("[FRIEND]: Found {0} regions to send to.", friendRegions.Count);
1052
1053 // clear uuids list and collect missed friends in it for the next retry
1054 friendIDsToSendTo.Clear();
1055
1056 // send bulk updates to the region
1057 foreach (KeyValuePair<ulong, List<UUID>> pair in friendsInRegion)
1058 {
1059 //m_log.DebugFormat("[FRIEND]: Inform {0} friends in region {1} that user {2} is {3}line",
1060 // pair.Value.Count, pair.Key, client.Name, iAmOnline ? "on" : "off");
1061
1062 friendIDsToSendTo.AddRange(InformFriendsInOtherRegion(client.AgentId, pair.Key, pair.Value, iAmOnline));
1063 } 601 }
602 if (friendClient != null) // Local, we're done
603 return;
1064 } 604 }
1065 // now we have in friendIDsToSendTo only the agents left that TPed away while we tried to contact them.
1066 // In most cases, it will be empty, and it won't loop here. But sometimes, we have to work harder and try again...
1067 } 605 }
1068 while (++retry < 3 && friendIDsToSendTo.Count > 0);
1069 }
1070 606
1071 private void OnEconomyDataRequest(UUID agentID) 607 PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { target.ToString() });
1072 { 608 PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions);
1073 // KLUDGE: This is the only way I found to get a message (only) after login was completed and the 609 if (friendSession != null)
1074 // client is connected enough to receive UDP packets).
1075 // This packet seems to be sent only once, just after connection was established to the first
1076 // region after login.
1077 // We use it here to trigger a presence update; the old update-on-login was never be heard by
1078 // the freshly logged in viewer, as it wasn't connected to the region at that time.
1079 // TODO: Feel free to replace this by a better solution if you find one.
1080
1081 // get the agent. This should work every time, as we just got a packet from it
1082 //ScenePresence agent = GetRootPresenceFromAgentID(agentID);
1083 // KLUDGE 2: As this is sent quite early, the avatar isn't here as root agent yet. So, we have to cheat a bit
1084 ScenePresence agent = GetAnyPresenceFromAgentID(agentID);
1085
1086 // just to be paranoid...
1087 if (agent == null)
1088 { 610 {
1089 m_log.ErrorFormat("[FRIEND]: Got a packet from agent {0} who can't be found anymore!?", agentID); 611 GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
1090 return; 612 // TODO: You might want to send the delta to save the lookup
613 // on the other end!!
614 m_FriendsSimConnector.GrantRights(region, requester, target);
1091 } 615 }
616 }
617
618 #region Local
1092 619
1093 List<FriendListItem> fl; 620 public bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
1094 lock (m_friendLists) 621 {
622 IClientAPI friendClient = LocateClientObject(toID);
623 if (friendClient != null)
1095 { 624 {
1096 fl = (List<FriendListItem>)m_friendLists.Get(agent.ControllingClient.AgentId.ToString(), 625 // the prospective friend in this sim as root agent
1097 m_initialScene.GetFriendList); 626 friendClient.SendInstantMessage(im);
627 // we're done
628 return true;
1098 } 629 }
1099 630 return false;
1100 // tell everyone that we are online
1101 SendPresenceState(agent.ControllingClient, fl, true);
1102 } 631 }
1103 632
1104 private void OnLogout(IClientAPI remoteClient) 633 public bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID)
1105 { 634 {
1106 List<FriendListItem> fl; 635 IClientAPI friendClient = LocateClientObject(friendID);
1107 lock (m_friendLists) 636 if (friendClient != null)
1108 { 637 {
1109 fl = (List<FriendListItem>)m_friendLists.Get(remoteClient.AgentId.ToString(), 638 // the prospective friend in this sim as root agent
1110 m_initialScene.GetFriendList); 639 GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
640 (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, userID.ToString(), false, Vector3.Zero);
641 friendClient.SendInstantMessage(im);
642 // update the local cache
643 m_Friends[friendID].Friends = FriendsService.GetFriends(friendID);
644 // we're done
645 return true;
1111 } 646 }
1112 647
1113 // tell everyone that we are offline 648 return false;
1114 SendPresenceState(remoteClient, fl, false);
1115 } 649 }
1116 private void GrantUserFriendRights(IClientAPI remoteClient, UUID requester, UUID target, int rights) 650
651 public bool LocalFriendshipDenied(UUID userID, string userName, UUID friendID)
1117 { 652 {
1118 ((Scene)remoteClient.Scene).CommsManager.UpdateUserFriendPerms(requester, target, (uint)rights); 653 IClientAPI friendClient = LocateClientObject(friendID);
654 if (friendClient != null)
655 {
656 // the prospective friend in this sim as root agent
657
658 GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID,
659 (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, userID.ToString(), false, Vector3.Zero);
660 friendClient.SendInstantMessage(im);
661 // we're done
662 return true;
663 }
664
665 return false;
1119 } 666 }
1120 public void FindAgent(IClientAPI remoteClient, UUID hunter, UUID target) 667
668 public bool LocalFriendshipTerminated(UUID exfriendID)
1121 { 669 {
1122 List<FriendListItem> friendList = GetUserFriends(hunter); 670 IClientAPI friendClient = LocateClientObject(exfriendID);
1123 foreach (FriendListItem item in friendList) 671 if (friendClient != null)
1124 { 672 {
1125 if (item.onlinestatus == true) 673 // the friend in this sim as root agent
1126 { 674 friendClient.SendTerminateFriend(exfriendID);
1127 if (item.Friend == target && (item.FriendPerms & (uint)FriendRights.CanSeeOnMap) != 0) 675 // update local cache
1128 { 676 m_Friends[exfriendID].Friends = FriendsService.GetFriends(exfriendID);
1129 ScenePresence SPTarget = ((Scene)remoteClient.Scene).GetScenePresence(target); 677 // we're done
1130 string regionname = SPTarget.Scene.RegionInfo.RegionName; 678 return true;
1131 remoteClient.SendScriptTeleportRequest("FindAgent", regionname,new Vector3(SPTarget.AbsolutePosition),new Vector3(SPTarget.Lookat));
1132 }
1133 }
1134 else
1135 {
1136 remoteClient.SendAgentAlertMessage("The agent you are looking for is not online.", false);
1137 }
1138 } 679 }
680
681 return false;
682 }
683
684 public bool LocalGrantRights()
685 {
686 // TODO
687 return true;
1139 } 688 }
1140 689
1141 public List<FriendListItem> GetUserFriends(UUID agentID) 690 public bool LocalStatusNotification(UUID userID, UUID friendID, bool online)
1142 { 691 {
1143 List<FriendListItem> fl; 692 IClientAPI friendClient = LocateClientObject(friendID);
1144 lock (m_friendLists) 693 if (friendClient != null)
1145 { 694 {
1146 fl = (List<FriendListItem>)m_friendLists.Get(agentID.ToString(), 695 //m_log.DebugFormat("[FRIENDS]: Notify {0} that user {1} is {2}", friend.Friend, userID, online);
1147 m_initialScene.GetFriendList); 696 // the friend in this sim as root agent
697 if (online)
698 friendClient.SendAgentOnline(new UUID[] { userID });
699 else
700 friendClient.SendAgentOffline(new UUID[] { userID });
701 // we're done
702 return true;
1148 } 703 }
1149 704
1150 return fl; 705 return false;
1151 } 706 }
707 #endregion
708
1152 } 709 }
1153 #endregion
1154} 710}
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs
new file mode 100644
index 0000000..e7b74a9
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs
@@ -0,0 +1,285 @@
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.Generic;
30using System.IO;
31using System.Reflection;
32using System.Xml;
33
34using OpenSim.Framework;
35using OpenSim.Server.Base;
36using OpenSim.Framework.Servers.HttpServer;
37
38using OpenMetaverse;
39using log4net;
40
41namespace OpenSim.Region.CoreModules.Avatar.Friends
42{
43 public class FriendsRequestHandler : BaseStreamHandler
44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 private FriendsModule m_FriendsModule;
48
49 public FriendsRequestHandler(FriendsModule fmodule)
50 : base("POST", "/friends")
51 {
52 m_FriendsModule = fmodule;
53 }
54
55 public override byte[] Handle(string path, Stream requestData,
56 OSHttpRequest httpRequest, OSHttpResponse httpResponse)
57 {
58 StreamReader sr = new StreamReader(requestData);
59 string body = sr.ReadToEnd();
60 sr.Close();
61 body = body.Trim();
62
63 m_log.DebugFormat("[XXX]: query String: {0}", body);
64
65 try
66 {
67 Dictionary<string, object> request =
68 ServerUtils.ParseQueryString(body);
69
70 if (!request.ContainsKey("METHOD"))
71 return FailureResult();
72
73 string method = request["METHOD"].ToString();
74 request.Remove("METHOD");
75
76 switch (method)
77 {
78 case "friendship_offered":
79 return FriendshipOffered(request);
80 case "friendship_approved":
81 return FriendshipApproved(request);
82 case "friendship_denied":
83 return FriendshipDenied(request);
84 case "friendship_terminated":
85 return FriendshipTerminated(request);
86 case "grant_rights":
87 return GrantRights(request);
88 case "status":
89 return StatusNotification(request);
90 }
91 }
92 catch (Exception e)
93 {
94 m_log.Debug("[FRIENDS]: Exception {0}" + e.ToString());
95 }
96
97 return FailureResult();
98 }
99
100 byte[] FriendshipOffered(Dictionary<string, object> request)
101 {
102 UUID fromID = UUID.Zero;
103 UUID toID = UUID.Zero;
104 string message = string.Empty;
105
106 if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID"))
107 return FailureResult();
108
109 message = request["Message"].ToString();
110
111 if (!UUID.TryParse(request["FromID"].ToString(), out fromID))
112 return FailureResult();
113
114 if (!UUID.TryParse(request["ToID"].ToString(), out toID))
115 return FailureResult();
116
117 GridInstantMessage im = new GridInstantMessage(m_FriendsModule.Scene, fromID, "", toID,
118 (byte)InstantMessageDialog.FriendshipOffered, message, false, Vector3.Zero);
119
120 if (m_FriendsModule.LocalFriendshipOffered(toID, im))
121 return SuccessResult();
122
123 return FailureResult();
124 }
125
126 byte[] FriendshipApproved(Dictionary<string, object> request)
127 {
128 UUID fromID = UUID.Zero;
129 UUID toID = UUID.Zero;
130 string fromName = string.Empty;
131
132 if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID"))
133 return FailureResult();
134
135 if (!UUID.TryParse(request["FromID"].ToString(), out fromID))
136 return FailureResult();
137
138 if (!UUID.TryParse(request["ToID"].ToString(), out toID))
139 return FailureResult();
140
141 if (request.ContainsKey("FromName"))
142 fromName = request["FromName"].ToString();
143
144 if (m_FriendsModule.LocalFriendshipApproved(fromID, fromName, toID))
145 return SuccessResult();
146
147 return FailureResult();
148 }
149
150 byte[] FriendshipDenied(Dictionary<string, object> request)
151 {
152 UUID fromID = UUID.Zero;
153 UUID toID = UUID.Zero;
154 string fromName = string.Empty;
155
156 if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID"))
157 return FailureResult();
158
159 if (!UUID.TryParse(request["FromID"].ToString(), out fromID))
160 return FailureResult();
161
162 if (!UUID.TryParse(request["ToID"].ToString(), out toID))
163 return FailureResult();
164
165 if (request.ContainsKey("FromName"))
166 fromName = request["FromName"].ToString();
167
168 if (m_FriendsModule.LocalFriendshipDenied(fromID, fromName, toID))
169 return SuccessResult();
170
171 return FailureResult();
172 }
173
174 byte[] FriendshipTerminated(Dictionary<string, object> request)
175 {
176 UUID fromID = UUID.Zero;
177 UUID toID = UUID.Zero;
178
179 if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID"))
180 return FailureResult();
181
182 if (!UUID.TryParse(request["FromID"].ToString(), out fromID))
183 return FailureResult();
184
185 if (!UUID.TryParse(request["ToID"].ToString(), out toID))
186 return FailureResult();
187
188 if (m_FriendsModule.LocalFriendshipTerminated(toID))
189 return SuccessResult();
190
191 return FailureResult();
192 }
193
194 byte[] GrantRights(Dictionary<string, object> request)
195 {
196 UUID fromID = UUID.Zero;
197 UUID toID = UUID.Zero;
198
199 if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID"))
200 return FailureResult();
201
202 if (!UUID.TryParse(request["FromID"].ToString(), out fromID))
203 return FailureResult();
204
205 if (!UUID.TryParse(request["ToID"].ToString(), out toID))
206 return FailureResult();
207
208 if (m_FriendsModule.LocalGrantRights(/* ??? */))
209 return SuccessResult();
210
211 return FailureResult();
212 }
213
214 byte[] StatusNotification(Dictionary<string, object> request)
215 {
216 UUID fromID = UUID.Zero;
217 UUID toID = UUID.Zero;
218 bool online = false;
219
220 if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID") || !request.ContainsKey("Online"))
221 return FailureResult();
222
223 if (!UUID.TryParse(request["FromID"].ToString(), out fromID))
224 return FailureResult();
225
226 if (!UUID.TryParse(request["ToID"].ToString(), out toID))
227 return FailureResult();
228
229 if (!Boolean.TryParse(request["Online"].ToString(), out online))
230 return FailureResult();
231
232 if (m_FriendsModule.LocalStatusNotification(fromID, toID, online))
233 return SuccessResult();
234
235 return FailureResult();
236 }
237
238 #region Misc
239
240 private byte[] FailureResult()
241 {
242 return BoolResult(false);
243 }
244
245 private byte[] SuccessResult()
246 {
247 return BoolResult(true);
248 }
249
250 private byte[] BoolResult(bool value)
251 {
252 XmlDocument doc = new XmlDocument();
253
254 XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration,
255 "", "");
256
257 doc.AppendChild(xmlnode);
258
259 XmlElement rootElement = doc.CreateElement("", "ServerResponse",
260 "");
261
262 doc.AppendChild(rootElement);
263
264 XmlElement result = doc.CreateElement("", "RESULT", "");
265 result.AppendChild(doc.CreateTextNode(value.ToString()));
266
267 rootElement.AppendChild(result);
268
269 return DocToBytes(doc);
270 }
271
272 private byte[] DocToBytes(XmlDocument doc)
273 {
274 MemoryStream ms = new MemoryStream();
275 XmlTextWriter xw = new XmlTextWriter(ms, null);
276 xw.Formatting = Formatting.Indented;
277 doc.WriteTo(xw);
278 xw.Flush();
279
280 return ms.ToArray();
281 }
282
283 #endregion
284 }
285}