opensim-development – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | eva | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ |
||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
||
4 | * |
||
5 | * Redistribution and use in source and binary forms, with or without |
||
6 | * modification, are permitted provided that the following conditions are met: |
||
7 | * * Redistributions of source code must retain the above copyright |
||
8 | * notice, this list of conditions and the following disclaimer. |
||
9 | * * Redistributions in binary form must reproduce the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer in the |
||
11 | * documentation and/or other materials provided with the distribution. |
||
12 | * * Neither the name of the OpenSimulator Project nor the |
||
13 | * names of its contributors may be used to endorse or promote products |
||
14 | * derived from this software without specific prior written permission. |
||
15 | * |
||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY |
||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY |
||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
26 | */ |
||
27 | |||
28 | using System; |
||
29 | using System.Collections.Generic; |
||
30 | using System.Net; |
||
31 | using System.Reflection; |
||
32 | |||
33 | using OpenSim.Framework; |
||
34 | using OpenSim.Services.Connectors.Friends; |
||
35 | using OpenSim.Services.Connectors.Hypergrid; |
||
36 | using OpenSim.Services.Interfaces; |
||
37 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; |
||
38 | using OpenSim.Server.Base; |
||
39 | using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; |
||
40 | |||
41 | using OpenMetaverse; |
||
42 | using log4net; |
||
43 | using Nini.Config; |
||
44 | |||
45 | namespace OpenSim.Services.HypergridService |
||
46 | { |
||
47 | /// <summary> |
||
48 | /// W2W social networking |
||
49 | /// </summary> |
||
50 | public class HGFriendsService : IHGFriendsService |
||
51 | { |
||
52 | private static readonly ILog m_log = |
||
53 | LogManager.GetLogger( |
||
54 | MethodBase.GetCurrentMethod().DeclaringType); |
||
55 | |||
56 | static bool m_Initialized = false; |
||
57 | |||
58 | protected static IGridUserService m_GridUserService; |
||
59 | protected static IGridService m_GridService; |
||
60 | protected static IGatekeeperService m_GatekeeperService; |
||
61 | protected static IFriendsService m_FriendsService; |
||
62 | protected static IPresenceService m_PresenceService; |
||
63 | protected static IUserAccountService m_UserAccountService; |
||
64 | protected static IFriendsSimConnector m_FriendsLocalSimConnector; // standalone, points to HGFriendsModule |
||
65 | protected static FriendsSimConnector m_FriendsSimConnector; // grid |
||
66 | |||
67 | private static string m_ConfigName = "HGFriendsService"; |
||
68 | |||
69 | public HGFriendsService(IConfigSource config, String configName, IFriendsSimConnector localSimConn) |
||
70 | { |
||
71 | if (m_FriendsLocalSimConnector == null) |
||
72 | m_FriendsLocalSimConnector = localSimConn; |
||
73 | |||
74 | if (!m_Initialized) |
||
75 | { |
||
76 | m_Initialized = true; |
||
77 | |||
78 | if (configName != String.Empty) |
||
79 | m_ConfigName = configName; |
||
80 | |||
81 | Object[] args = new Object[] { config }; |
||
82 | |||
83 | IConfig serverConfig = config.Configs[m_ConfigName]; |
||
84 | if (serverConfig == null) |
||
85 | throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); |
||
86 | |||
87 | string theService = serverConfig.GetString("FriendsService", string.Empty); |
||
88 | if (theService == String.Empty) |
||
89 | throw new Exception("No FriendsService in config file " + m_ConfigName); |
||
90 | m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(theService, args); |
||
91 | |||
92 | theService = serverConfig.GetString("UserAccountService", string.Empty); |
||
93 | if (theService == String.Empty) |
||
94 | throw new Exception("No UserAccountService in " + m_ConfigName); |
||
95 | m_UserAccountService = ServerUtils.LoadPlugin<IUserAccountService>(theService, args); |
||
96 | |||
97 | theService = serverConfig.GetString("GridService", string.Empty); |
||
98 | if (theService == String.Empty) |
||
99 | throw new Exception("No GridService in " + m_ConfigName); |
||
100 | m_GridService = ServerUtils.LoadPlugin<IGridService>(theService, args); |
||
101 | |||
102 | theService = serverConfig.GetString("PresenceService", string.Empty); |
||
103 | if (theService == String.Empty) |
||
104 | throw new Exception("No PresenceService in " + m_ConfigName); |
||
105 | m_PresenceService = ServerUtils.LoadPlugin<IPresenceService>(theService, args); |
||
106 | |||
107 | m_FriendsSimConnector = new FriendsSimConnector(); |
||
108 | |||
109 | m_log.DebugFormat("[HGFRIENDS SERVICE]: Starting..."); |
||
110 | |||
111 | } |
||
112 | } |
||
113 | |||
114 | #region IHGFriendsService |
||
115 | |||
116 | public int GetFriendPerms(UUID userID, UUID friendID) |
||
117 | { |
||
118 | FriendInfo[] friendsInfo = m_FriendsService.GetFriends(userID); |
||
119 | foreach (FriendInfo finfo in friendsInfo) |
||
120 | { |
||
121 | if (finfo.Friend.StartsWith(friendID.ToString())) |
||
122 | return finfo.TheirFlags; |
||
123 | } |
||
124 | return -1; |
||
125 | } |
||
126 | |||
127 | public bool NewFriendship(FriendInfo friend, bool verified) |
||
128 | { |
||
129 | UUID friendID; |
||
130 | string tmp = string.Empty, url = String.Empty, first = String.Empty, last = String.Empty; |
||
131 | if (!Util.ParseUniversalUserIdentifier(friend.Friend, out friendID, out url, out first, out last, out tmp)) |
||
132 | return false; |
||
133 | |||
134 | m_log.DebugFormat("[HGFRIENDS SERVICE]: New friendship {0} {1} ({2})", friend.PrincipalID, friend.Friend, verified); |
||
135 | |||
136 | // Does the friendship already exist? |
||
137 | FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); |
||
138 | foreach (FriendInfo finfo in finfos) |
||
139 | { |
||
140 | if (finfo.Friend.StartsWith(friendID.ToString())) |
||
141 | return false; |
||
142 | } |
||
143 | // Verified user session. But the user needs to confirm friendship when he gets home |
||
144 | if (verified) |
||
145 | return m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 0); |
||
146 | |||
147 | // Does the reverted friendship exist? meaning that this user initiated the request |
||
148 | finfos = m_FriendsService.GetFriends(friendID); |
||
149 | bool userInitiatedOffer = false; |
||
150 | foreach (FriendInfo finfo in finfos) |
||
151 | { |
||
152 | if (friend.Friend.StartsWith(finfo.PrincipalID.ToString()) && finfo.Friend.StartsWith(friend.PrincipalID.ToString()) && finfo.TheirFlags == -1) |
||
153 | { |
||
154 | userInitiatedOffer = true; |
||
155 | // Let's delete the existing friendship relations that was stored |
||
156 | m_FriendsService.Delete(friendID, finfo.Friend); |
||
157 | break; |
||
158 | } |
||
159 | } |
||
160 | |||
161 | if (userInitiatedOffer) |
||
162 | { |
||
163 | m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 1); |
||
164 | m_FriendsService.StoreFriend(friend.Friend, friend.PrincipalID.ToString(), 1); |
||
165 | // notify the user |
||
166 | ForwardToSim("ApproveFriendshipRequest", friendID, Util.UniversalName(first, last, url), "", friend.PrincipalID, ""); |
||
167 | return true; |
||
168 | } |
||
169 | return false; |
||
170 | } |
||
171 | |||
172 | public bool DeleteFriendship(FriendInfo friend, string secret) |
||
173 | { |
||
174 | FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); |
||
175 | foreach (FriendInfo finfo in finfos) |
||
176 | { |
||
177 | // We check the secret here. Or if the friendship request was initiated here, and was declined |
||
178 | if (finfo.Friend.StartsWith(friend.Friend) && finfo.Friend.EndsWith(secret)) |
||
179 | { |
||
180 | m_log.DebugFormat("[HGFRIENDS SERVICE]: Delete friendship {0} {1}", friend.PrincipalID, friend.Friend); |
||
181 | m_FriendsService.Delete(friend.PrincipalID, finfo.Friend); |
||
182 | m_FriendsService.Delete(finfo.Friend, friend.PrincipalID.ToString()); |
||
183 | |||
184 | return true; |
||
185 | } |
||
186 | } |
||
187 | |||
188 | return false; |
||
189 | } |
||
190 | |||
191 | public bool FriendshipOffered(UUID fromID, string fromName, UUID toID, string message) |
||
192 | { |
||
193 | UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, toID); |
||
194 | if (account == null) |
||
195 | return false; |
||
196 | |||
197 | // OK, we have that user here. |
||
198 | // So let's send back the call, but start a thread to continue |
||
199 | // with the verification and the actual action. |
||
200 | |||
201 | Util.FireAndForget(delegate { ProcessFriendshipOffered(fromID, fromName, toID, message); }); |
||
202 | |||
203 | return true; |
||
204 | } |
||
205 | |||
206 | public bool ValidateFriendshipOffered(UUID fromID, UUID toID) |
||
207 | { |
||
208 | FriendInfo[] finfos = m_FriendsService.GetFriends(toID.ToString()); |
||
209 | foreach (FriendInfo fi in finfos) |
||
210 | { |
||
211 | if (fi.Friend.StartsWith(fromID.ToString()) && fi.TheirFlags == -1) |
||
212 | return true; |
||
213 | } |
||
214 | return false; |
||
215 | } |
||
216 | |||
217 | public List<UUID> StatusNotification(List<string> friends, UUID foreignUserID, bool online) |
||
218 | { |
||
219 | if (m_FriendsService == null || m_PresenceService == null) |
||
220 | { |
||
221 | m_log.WarnFormat("[HGFRIENDS SERVICE]: Unable to perform status notifications because friends or presence services are missing"); |
||
222 | return new List<UUID>(); |
||
223 | } |
||
224 | |||
225 | // Let's unblock the caller right now, and take it from here async |
||
226 | |||
227 | List<UUID> localFriendsOnline = new List<UUID>(); |
||
228 | |||
229 | m_log.DebugFormat("[HGFRIENDS SERVICE]: Status notification: foreign user {0} wants to notify {1} local friends of {2} status", |
||
230 | foreignUserID, friends.Count, (online ? "online" : "offline")); |
||
231 | |||
232 | // First, let's double check that the reported friends are, indeed, friends of that user |
||
233 | // And let's check that the secret matches |
||
234 | List<string> usersToBeNotified = new List<string>(); |
||
235 | foreach (string uui in friends) |
||
236 | { |
||
237 | UUID localUserID; |
||
238 | string secret = string.Empty, tmp = string.Empty; |
||
239 | if (Util.ParseUniversalUserIdentifier(uui, out localUserID, out tmp, out tmp, out tmp, out secret)) |
||
240 | { |
||
241 | FriendInfo[] friendInfos = m_FriendsService.GetFriends(localUserID); |
||
242 | foreach (FriendInfo finfo in friendInfos) |
||
243 | { |
||
244 | if (finfo.Friend.StartsWith(foreignUserID.ToString()) && finfo.Friend.EndsWith(secret)) |
||
245 | { |
||
246 | // great! |
||
247 | usersToBeNotified.Add(localUserID.ToString()); |
||
248 | } |
||
249 | } |
||
250 | } |
||
251 | } |
||
252 | |||
253 | // Now, let's send the notifications |
||
254 | //m_log.DebugFormat("[HGFRIENDS SERVICE]: Status notification: user has {0} local friends", usersToBeNotified.Count); |
||
255 | |||
256 | // First, let's send notifications to local users who are online in the home grid |
||
257 | PresenceInfo[] friendSessions = m_PresenceService.GetAgents(usersToBeNotified.ToArray()); |
||
258 | if (friendSessions != null && friendSessions.Length > 0) |
||
259 | { |
||
260 | PresenceInfo friendSession = null; |
||
261 | foreach (PresenceInfo pinfo in friendSessions) |
||
262 | if (pinfo.RegionID != UUID.Zero) // let's guard against traveling agents |
||
263 | { |
||
264 | friendSession = pinfo; |
||
265 | break; |
||
266 | } |
||
267 | |||
268 | if (friendSession != null) |
||
269 | { |
||
270 | ForwardStatusNotificationToSim(friendSession.RegionID, foreignUserID, friendSession.UserID, online); |
||
271 | usersToBeNotified.Remove(friendSession.UserID.ToString()); |
||
272 | UUID id; |
||
273 | if (UUID.TryParse(friendSession.UserID, out id)) |
||
274 | localFriendsOnline.Add(id); |
||
275 | |||
276 | } |
||
277 | } |
||
278 | |||
279 | // // Lastly, let's notify the rest who may be online somewhere else |
||
280 | // foreach (string user in usersToBeNotified) |
||
281 | // { |
||
282 | // UUID id = new UUID(user); |
||
283 | // //m_UserAgentService.LocateUser(id); |
||
284 | // //etc... |
||
285 | // //if (m_TravelingAgents.ContainsKey(id) && m_TravelingAgents[id].GridExternalName != m_GridName) |
||
286 | // //{ |
||
287 | // // string url = m_TravelingAgents[id].GridExternalName; |
||
288 | // // // forward |
||
289 | // //} |
||
290 | // //m_log.WarnFormat("[HGFRIENDS SERVICE]: User {0} is visiting another grid. HG Status notifications still not implemented.", user); |
||
291 | // } |
||
292 | |||
293 | // and finally, let's send the online friends |
||
294 | if (online) |
||
295 | { |
||
296 | return localFriendsOnline; |
||
297 | } |
||
298 | else |
||
299 | return new List<UUID>(); |
||
300 | } |
||
301 | |||
302 | #endregion IHGFriendsService |
||
303 | |||
304 | #region Aux |
||
305 | |||
306 | private void ProcessFriendshipOffered(UUID fromID, String fromName, UUID toID, String message) |
||
307 | { |
||
308 | // Great, it's a genuine request. Let's proceed. |
||
309 | // But now we need to confirm that the requester is who he says he is |
||
310 | // before we act on the friendship request. |
||
311 | |||
312 | if (!fromName.Contains("@")) |
||
313 | return; |
||
314 | |||
315 | string[] parts = fromName.Split(new char[] {'@'}); |
||
316 | if (parts.Length != 2) |
||
317 | return; |
||
318 | |||
319 | string uriStr = "http://" + parts[1]; |
||
320 | try |
||
321 | { |
||
322 | new Uri(uriStr); |
||
323 | } |
||
324 | catch (UriFormatException) |
||
325 | { |
||
326 | return; |
||
327 | } |
||
328 | |||
329 | UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr); |
||
330 | Dictionary<string, object> servers = uasConn.GetServerURLs(fromID); |
||
331 | if (!servers.ContainsKey("FriendsServerURI")) |
||
332 | return; |
||
333 | |||
334 | HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(servers["FriendsServerURI"].ToString()); |
||
335 | if (!friendsConn.ValidateFriendshipOffered(fromID, toID)) |
||
336 | { |
||
337 | m_log.WarnFormat("[HGFRIENDS SERVICE]: Friendship request from {0} to {1} is invalid. Impersonations?", fromID, toID); |
||
338 | return; |
||
339 | } |
||
340 | |||
341 | string fromUUI = Util.UniversalIdentifier(fromID, parts[0], "@" + parts[1], uriStr); |
||
342 | // OK, we're good! |
||
343 | ForwardToSim("FriendshipOffered", fromID, fromName, fromUUI, toID, message); |
||
344 | } |
||
345 | |||
346 | private bool ForwardToSim(string op, UUID fromID, string name, String fromUUI, UUID toID, string message) |
||
347 | { |
||
348 | PresenceInfo session = null; |
||
349 | GridRegion region = null; |
||
350 | PresenceInfo[] sessions = m_PresenceService.GetAgents(new string[] { toID.ToString() }); |
||
351 | if (sessions != null && sessions.Length > 0) |
||
352 | session = sessions[0]; |
||
353 | if (session != null) |
||
354 | region = m_GridService.GetRegionByUUID(UUID.Zero, session.RegionID); |
||
355 | |||
356 | switch (op) |
||
357 | { |
||
358 | case "FriendshipOffered": |
||
359 | // Let's store backwards |
||
360 | string secret = UUID.Random().ToString().Substring(0, 8); |
||
361 | m_FriendsService.StoreFriend(toID.ToString(), fromUUI + ";" + secret, 0); |
||
362 | if (m_FriendsLocalSimConnector != null) // standalone |
||
363 | { |
||
364 | GridInstantMessage im = new GridInstantMessage(null, fromID, name, toID, |
||
365 | (byte)InstantMessageDialog.FriendshipOffered, message, false, Vector3.Zero); |
||
366 | // !! HACK |
||
367 | im.imSessionID = im.fromAgentID; |
||
368 | return m_FriendsLocalSimConnector.LocalFriendshipOffered(toID, im); |
||
369 | } |
||
370 | else if (region != null) // grid |
||
371 | return m_FriendsSimConnector.FriendshipOffered(region, fromID, toID, message, name); |
||
372 | break; |
||
373 | case "ApproveFriendshipRequest": |
||
374 | if (m_FriendsLocalSimConnector != null) // standalone |
||
375 | return m_FriendsLocalSimConnector.LocalFriendshipApproved(fromID, name, toID); |
||
376 | else if (region != null) //grid |
||
377 | return m_FriendsSimConnector.FriendshipApproved(region, fromID, name, toID); |
||
378 | break; |
||
379 | } |
||
380 | |||
381 | return false; |
||
382 | } |
||
383 | |||
384 | protected void ForwardStatusNotificationToSim(UUID regionID, UUID foreignUserID, string user, bool online) |
||
385 | { |
||
386 | UUID userID; |
||
387 | if (UUID.TryParse(user, out userID)) |
||
388 | { |
||
389 | if (m_FriendsLocalSimConnector != null) |
||
390 | { |
||
391 | m_log.DebugFormat("[HGFRIENDS SERVICE]: Local Notify, user {0} is {1}", foreignUserID, (online ? "online" : "offline")); |
||
392 | m_FriendsLocalSimConnector.StatusNotify(foreignUserID, userID, online); |
||
393 | } |
||
394 | else |
||
395 | { |
||
396 | GridRegion region = m_GridService.GetRegionByUUID(UUID.Zero /* !!! */, regionID); |
||
397 | if (region != null) |
||
398 | { |
||
399 | m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline")); |
||
400 | m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online); |
||
401 | } |
||
402 | } |
||
403 | } |
||
404 | } |
||
405 | |||
406 | #endregion Aux |
||
407 | } |
||
408 | } |