aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs504
1 files changed, 504 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
new file mode 100644
index 0000000..3b0cc4c
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
@@ -0,0 +1,504 @@
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 OpenSim 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 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using libsecondlife;
31using libsecondlife.Packets;
32using log4net;
33using Nini.Config;
34using Nwc.XmlRpc;
35using OpenSim.Framework;
36using OpenSim.Region.Environment.Interfaces;
37using OpenSim.Region.Environment.Scenes;
38
39namespace OpenSim.Region.Environment.Modules.Avatar.Friends
40{
41 public class FriendsModule : IRegionModule
42 {
43 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44
45 private List<Scene> m_scene = new List<Scene>();
46
47 Dictionary<LLUUID, ulong> m_rootAgents = new Dictionary<LLUUID, ulong>();
48
49 Dictionary<LLUUID, LLUUID> m_pendingFriendRequests = new Dictionary<LLUUID, LLUUID>();
50
51 Dictionary<LLUUID, List<FriendListItem>> FriendLists = new Dictionary<LLUUID, List<FriendListItem>>();
52
53 public void Initialise(Scene scene, IConfigSource config)
54 {
55 lock (m_scene)
56 {
57 if (m_scene.Count == 0)
58 {
59 scene.AddXmlRPCHandler("presence_update", processPresenceUpdate);
60 }
61
62 if (!m_scene.Contains(scene))
63 m_scene.Add(scene);
64 }
65 scene.EventManager.OnNewClient += OnNewClient;
66 scene.EventManager.OnGridInstantMessageToFriendsModule += OnGridInstantMessage;
67 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
68 scene.EventManager.OnMakeChildAgent += MakeChildAgent;
69 scene.EventManager.OnClientClosed += ClientLoggedOut;
70 }
71 public XmlRpcResponse processPresenceUpdate(XmlRpcRequest req)
72 {
73 m_log.Info("[FRIENDS]: Got Notification about a user! OMG");
74 return new XmlRpcResponse();
75 }
76 private void OnNewClient(IClientAPI client)
77 {
78 // All friends establishment protocol goes over instant message
79 // There's no way to send a message from the sim
80 // to a user to 'add a friend' without causing dialog box spam
81 //
82 // The base set of friends are added when the user signs on in their XMLRPC response
83 // Generated by LoginService. The friends are retreived from the database by the UserManager
84
85 // Subscribe to instant messages
86
87 client.OnInstantMessage += OnInstantMessage;
88 client.OnApproveFriendRequest += OnApprovedFriendRequest;
89 client.OnDenyFriendRequest += OnDenyFriendRequest;
90 client.OnTerminateFriendship += OnTerminateFriendship;
91
92 List<FriendListItem> fl = new List<FriendListItem>();
93
94 //bool addFLback = false;
95
96 lock (FriendLists)
97 {
98 if (FriendLists.ContainsKey(client.AgentId))
99 {
100 fl = FriendLists[client.AgentId];
101 }
102 else
103 {
104 fl = m_scene[0].GetFriendList(client.AgentId);
105
106 //lock (FriendLists)
107 //{
108 if (!FriendLists.ContainsKey(client.AgentId))
109 FriendLists.Add(client.AgentId, fl);
110 //}
111 }
112 }
113
114 List<LLUUID> UpdateUsers = new List<LLUUID>();
115
116 foreach (FriendListItem f in fl)
117 {
118 if (m_rootAgents.ContainsKey(f.Friend))
119 {
120 if (f.onlinestatus == false)
121 {
122 UpdateUsers.Add(f.Friend);
123 f.onlinestatus = true;
124 }
125 }
126 }
127 foreach (LLUUID user in UpdateUsers)
128 {
129 ScenePresence av = GetPresenceFromAgentID(user);
130 if (av != null)
131 {
132 List<FriendListItem> usrfl = new List<FriendListItem>();
133
134 lock (FriendLists)
135 {
136 usrfl = FriendLists[user];
137 }
138
139 lock (usrfl)
140 {
141 foreach (FriendListItem fli in usrfl)
142 {
143 if (fli.Friend == client.AgentId)
144 {
145 fli.onlinestatus = true;
146 OnlineNotificationPacket onp = new OnlineNotificationPacket();
147 OnlineNotificationPacket.AgentBlockBlock[] onpb = new OnlineNotificationPacket.AgentBlockBlock[1];
148 OnlineNotificationPacket.AgentBlockBlock onpbl = new OnlineNotificationPacket.AgentBlockBlock();
149 onpbl.AgentID = client.AgentId;
150 onpb[0] = onpbl;
151 onp.AgentBlock = onpb;
152 av.ControllingClient.OutPacket(onp, ThrottleOutPacketType.Task);
153 }
154 }
155 }
156 }
157 }
158
159 if (UpdateUsers.Count > 0)
160 {
161 OnlineNotificationPacket onp = new OnlineNotificationPacket();
162 OnlineNotificationPacket.AgentBlockBlock[] onpb = new OnlineNotificationPacket.AgentBlockBlock[UpdateUsers.Count];
163 for (int i = 0; i < UpdateUsers.Count; i++)
164 {
165 OnlineNotificationPacket.AgentBlockBlock onpbl = new OnlineNotificationPacket.AgentBlockBlock();
166 onpbl.AgentID = UpdateUsers[i];
167 onpb[i] = onpbl;
168 }
169 onp.AgentBlock = onpb;
170 client.OutPacket(onp, ThrottleOutPacketType.Task);
171 }
172
173
174
175
176 }
177
178 private void ClientLoggedOut(LLUUID AgentId)
179 {
180 lock (m_rootAgents)
181 {
182 if (m_rootAgents.ContainsKey(AgentId))
183 {
184 m_rootAgents.Remove(AgentId);
185 m_log.Info("[FRIEND]: Removing " + AgentId + ". Agent logged out.");
186 }
187 }
188 List<FriendListItem> lfli = new List<FriendListItem>();
189 lock (FriendLists)
190 {
191 if (FriendLists.ContainsKey(AgentId))
192 {
193 lfli = FriendLists[AgentId];
194 }
195 }
196 List<LLUUID> updateUsers = new List<LLUUID>();
197 foreach (FriendListItem fli in lfli)
198 {
199 if (fli.onlinestatus == true)
200 {
201 updateUsers.Add(fli.Friend);
202 }
203 }
204 lock (updateUsers)
205 {
206 for (int i = 0; i < updateUsers.Count; i++)
207 {
208 List<FriendListItem> flfli = new List<FriendListItem>();
209 try
210 {
211
212 lock (FriendLists)
213 {
214 if (FriendLists.ContainsKey(updateUsers[i]))
215 flfli = FriendLists[updateUsers[i]];
216 }
217 }
218 catch (IndexOutOfRangeException)
219 {
220 // Ignore the index out of range exception.
221 // This causes friend lists to get out of sync slightly.. however
222 // prevents a sim crash.
223 m_log.Info("[FRIEND]: Unable to enumerate last friendlist user. User logged off");
224 }
225
226 for (int j = 0; j < flfli.Count; j++)
227 {
228 try
229 {
230 if (flfli[i].Friend == AgentId)
231 {
232 flfli[i].onlinestatus = false;
233 }
234
235 }
236
237 catch (IndexOutOfRangeException)
238 {
239 // Ignore the index out of range exception.
240 // This causes friend lists to get out of sync slightly.. however
241 // prevents a sim crash.
242 m_log.Info("[FRIEND]: Unable to enumerate last friendlist user. User logged off");
243 }
244 }
245
246 }
247
248 for (int i = 0; i < updateUsers.Count; i++)
249 {
250 ScenePresence av = GetPresenceFromAgentID(updateUsers[i]);
251 if (av != null)
252 {
253
254 OfflineNotificationPacket onp = new OfflineNotificationPacket();
255 OfflineNotificationPacket.AgentBlockBlock[] onpb = new OfflineNotificationPacket.AgentBlockBlock[1];
256 OfflineNotificationPacket.AgentBlockBlock onpbl = new OfflineNotificationPacket.AgentBlockBlock();
257 onpbl.AgentID = AgentId;
258 onpb[0] = onpbl;
259 onp.AgentBlock = onpb;
260 av.ControllingClient.OutPacket(onp, ThrottleOutPacketType.Task);
261 }
262 }
263 }
264 lock (FriendLists)
265 {
266 FriendLists.Remove(AgentId);
267 }
268
269 }
270
271 private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, LLUUID regionID)
272 {
273 lock (m_rootAgents)
274 {
275 if (m_rootAgents.ContainsKey(avatar.UUID))
276 {
277 if (avatar.RegionHandle != m_rootAgents[avatar.UUID])
278 {
279 m_rootAgents[avatar.UUID] = avatar.RegionHandle;
280 m_log.Info("[FRIEND]: Claiming " + avatar.Firstname + " " + avatar.Lastname + " in region:" + avatar.RegionHandle + ".");
281 if (avatar.JID.Length > 0)
282 {
283 JId avatarID = new JId(avatar.JID);
284 // REST Post XMPP Stanzas!
285
286 }
287 // Claim User! my user! Mine mine mine!
288 }
289 }
290 else
291 {
292 m_rootAgents.Add(avatar.UUID, avatar.RegionHandle);
293 m_log.Info("[FRIEND]: Claiming " + avatar.Firstname + " " + avatar.Lastname + " in region:" + avatar.RegionHandle + ".");
294 }
295 }
296 //m_log.Info("[FRIEND]: " + avatar.Name + " status:" + (!avatar.IsChildAgent).ToString());
297 }
298 private void MakeChildAgent(ScenePresence avatar)
299 {
300
301 lock (m_rootAgents)
302 {
303 if (m_rootAgents.ContainsKey(avatar.UUID))
304 {
305 if (m_rootAgents[avatar.UUID] == avatar.RegionHandle)
306 {
307 m_rootAgents.Remove(avatar.UUID);
308 m_log.Info("[FRIEND]: Removing " + avatar.Firstname + " " + avatar.Lastname + " as a root agent");
309 }
310
311 }
312 }
313
314 }
315 #region FriendRequestHandling
316 private void OnInstantMessage(IClientAPI client,LLUUID fromAgentID,
317 LLUUID fromAgentSession, LLUUID toAgentID,
318 LLUUID imSessionID, uint timestamp, string fromAgentName,
319 string message, byte dialog, bool fromGroup, byte offline,
320 uint ParentEstateID, LLVector3 Position, LLUUID RegionID,
321 byte[] binaryBucket)
322 {
323 // Friend Requests go by Instant Message.. using the dialog param
324 // https://wiki.secondlife.com/wiki/ImprovedInstantMessage
325
326 // 38 == Offer friendship
327 if (dialog == (byte)38)
328 {
329 LLUUID friendTransactionID = LLUUID.Random();
330
331 m_pendingFriendRequests.Add(friendTransactionID, fromAgentID);
332
333 m_log.Info("[FRIEND]: 38 - From:" + fromAgentID.ToString() + " To: " + toAgentID.ToString() + " Session:" + imSessionID.ToString() + " Message:" + message);
334 GridInstantMessage msg = new GridInstantMessage();
335 msg.fromAgentID = fromAgentID.UUID;
336 msg.fromAgentSession = fromAgentSession.UUID;
337 msg.toAgentID = toAgentID.UUID;
338 msg.imSessionID = friendTransactionID.UUID; // This is the item we're mucking with here
339 m_log.Info("[FRIEND]: Filling Session: " + msg.imSessionID.ToString());
340 msg.timestamp = timestamp;
341 if (client != null)
342 {
343 msg.fromAgentName = client.FirstName + " " + client.LastName;// fromAgentName;
344 }
345 else
346 {
347 msg.fromAgentName = "(hippos)";// Added for posterity. This means that we can't figure out who sent it
348 }
349 msg.message = message;
350 msg.dialog = dialog;
351 msg.fromGroup = fromGroup;
352 msg.offline = offline;
353 msg.ParentEstateID = ParentEstateID;
354 msg.Position = new sLLVector3(Position);
355 msg.RegionID = RegionID.UUID;
356 msg.binaryBucket = binaryBucket;
357 // We don't really care which scene we pipe it through.
358 m_scene[0].TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule);
359 }
360
361 // 39 == Accept Friendship
362 if (dialog == (byte)39)
363 {
364 m_log.Info("[FRIEND]: 39 - From:" + fromAgentID.ToString() + " To: " + toAgentID.ToString() + " Session:" + imSessionID.ToString() + " Message:" + message);
365 }
366
367 // 40 == Decline Friendship
368 if (dialog == (byte)40)
369 {
370 m_log.Info("[FRIEND]: 40 - From:" + fromAgentID.ToString() + " To: " + toAgentID.ToString() + " Session:" + imSessionID.ToString() + " Message:" + message);
371 }
372 }
373
374 private void OnApprovedFriendRequest(IClientAPI client, LLUUID agentID, LLUUID transactionID, List<LLUUID> callingCardFolders)
375 {
376 if (m_pendingFriendRequests.ContainsKey(transactionID))
377 {
378 // Found Pending Friend Request with that Transaction..
379 Scene SceneAgentIn = m_scene[0];
380
381 // Found Pending Friend Request with that Transaction..
382 ScenePresence agentpresence = GetPresenceFromAgentID(agentID);
383 if (agentpresence != null)
384 {
385 SceneAgentIn = agentpresence.Scene;
386 }
387
388 // Compose response to other agent.
389 GridInstantMessage msg = new GridInstantMessage();
390 msg.toAgentID = m_pendingFriendRequests[transactionID].UUID;
391 msg.fromAgentID = agentID.UUID;
392 msg.fromAgentName = client.FirstName + " " + client.LastName;
393 msg.fromAgentSession = client.SessionId.UUID;
394 msg.fromGroup = false;
395 msg.imSessionID = transactionID.UUID;
396 msg.message = agentID.UUID.ToString();
397 msg.ParentEstateID = 0;
398 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
399 msg.RegionID = SceneAgentIn.RegionInfo.RegionID.UUID;
400 msg.dialog = (byte)39;// Approved friend request
401 msg.Position = new sLLVector3();
402 msg.offline = (byte)0;
403 msg.binaryBucket = new byte[0];
404 // We don't really care which scene we pipe it through, it goes to the shared IM Module and/or the database
405
406 SceneAgentIn.TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule);
407 SceneAgentIn.StoreAddFriendship(m_pendingFriendRequests[transactionID], agentID, (uint)1);
408 m_pendingFriendRequests.Remove(transactionID);
409
410 // TODO: Inform agent that the friend is online
411 }
412 }
413
414 private void OnDenyFriendRequest(IClientAPI client, LLUUID agentID, LLUUID transactionID, List<LLUUID> callingCardFolders)
415 {
416 if (m_pendingFriendRequests.ContainsKey(transactionID))
417 {
418 Scene SceneAgentIn = m_scene[0];
419
420 // Found Pending Friend Request with that Transaction..
421 ScenePresence agentpresence = GetPresenceFromAgentID(agentID);
422 if (agentpresence != null)
423 {
424 SceneAgentIn = agentpresence.Scene;
425 }
426 // Compose response to other agent.
427 GridInstantMessage msg = new GridInstantMessage();
428 msg.toAgentID = m_pendingFriendRequests[transactionID].UUID;
429 msg.fromAgentID = agentID.UUID;
430 msg.fromAgentName = client.FirstName + " " + client.LastName;
431 msg.fromAgentSession = client.SessionId.UUID;
432 msg.fromGroup = false;
433 msg.imSessionID = transactionID.UUID;
434 msg.message = agentID.UUID.ToString();
435 msg.ParentEstateID = 0;
436 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
437 msg.RegionID = SceneAgentIn.RegionInfo.RegionID.UUID;
438 msg.dialog = (byte)40;// Deny friend request
439 msg.Position = new sLLVector3();
440 msg.offline = (byte)0;
441 msg.binaryBucket = new byte[0];
442 SceneAgentIn.TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule);
443 m_pendingFriendRequests.Remove(transactionID);
444 }
445 }
446
447 private void OnTerminateFriendship(IClientAPI client, LLUUID agent, LLUUID exfriendID)
448 {
449 m_scene[0].StoreRemoveFriendship(agent, exfriendID);
450 // TODO: Inform the client that the ExFriend is offline
451 }
452
453 private void OnGridInstantMessage(GridInstantMessage msg)
454 {
455 // Trigger the above event handler
456 OnInstantMessage(null,new LLUUID(msg.fromAgentID), new LLUUID(msg.fromAgentSession),
457 new LLUUID(msg.toAgentID), new LLUUID(msg.imSessionID), msg.timestamp, msg.fromAgentName,
458 msg.message, msg.dialog, msg.fromGroup, msg.offline, msg.ParentEstateID,
459 new LLVector3(msg.Position.x, msg.Position.y, msg.Position.z), new LLUUID(msg.RegionID),
460 msg.binaryBucket);
461 }
462 #endregion
463 private ScenePresence GetPresenceFromAgentID(LLUUID AgentID)
464 {
465 ScenePresence returnAgent = null;
466 lock (m_scene)
467 {
468 ScenePresence queryagent = null;
469 for (int i = 0; i < m_scene.Count; i++)
470 {
471 queryagent = m_scene[i].GetScenePresence(AgentID);
472 if (queryagent != null)
473 {
474 if (!queryagent.IsChildAgent)
475 {
476 returnAgent = queryagent;
477 break;
478 }
479 }
480 }
481 }
482 return returnAgent;
483
484 }
485
486 public void PostInitialise()
487 {
488 }
489
490 public void Close()
491 {
492 }
493
494 public string Name
495 {
496 get { return "FriendsModule"; }
497 }
498
499 public bool IsSharedModule
500 {
501 get { return true; }
502 }
503 }
504} \ No newline at end of file