OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
HGFriendsService.cs
Go to the documentation of this file.
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;
38 using OpenSim.Server.Base;
40 
41 using OpenMetaverse;
42 using log4net;
43 using Nini.Config;
44 
45 namespace OpenSim.Services.HypergridService
46 {
51  {
52  private static readonly ILog m_log =
53  LogManager.GetLogger(
54  MethodBase.GetCurrentMethod().DeclaringType);
55 
56  static bool m_Initialized = false;
57 
59  protected static IGridService m_GridService;
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(
202  o => ProcessFriendshipOffered(fromID, fromName, toID, message), null, "HGFriendsService.ProcessFriendshipOffered");
203 
204  return true;
205  }
206 
207  public bool ValidateFriendshipOffered(UUID fromID, UUID toID)
208  {
209  FriendInfo[] finfos = m_FriendsService.GetFriends(toID.ToString());
210  foreach (FriendInfo fi in finfos)
211  {
212  if (fi.Friend.StartsWith(fromID.ToString()) && fi.TheirFlags == -1)
213  return true;
214  }
215  return false;
216  }
217 
218  public List<UUID> StatusNotification(List<string> friends, UUID foreignUserID, bool online)
219  {
220  if (m_FriendsService == null || m_PresenceService == null)
221  {
222  m_log.WarnFormat("[HGFRIENDS SERVICE]: Unable to perform status notifications because friends or presence services are missing");
223  return new List<UUID>();
224  }
225 
226  // Let's unblock the caller right now, and take it from here async
227 
228  List<UUID> localFriendsOnline = new List<UUID>();
229 
230  m_log.DebugFormat("[HGFRIENDS SERVICE]: Status notification: foreign user {0} wants to notify {1} local friends of {2} status",
231  foreignUserID, friends.Count, (online ? "online" : "offline"));
232 
233  // First, let's double check that the reported friends are, indeed, friends of that user
234  // And let's check that the secret matches
235  List<string> usersToBeNotified = new List<string>();
236  foreach (string uui in friends)
237  {
238  UUID localUserID;
239  string secret = string.Empty, tmp = string.Empty;
240  if (Util.ParseUniversalUserIdentifier(uui, out localUserID, out tmp, out tmp, out tmp, out secret))
241  {
242  FriendInfo[] friendInfos = m_FriendsService.GetFriends(localUserID);
243  foreach (FriendInfo finfo in friendInfos)
244  {
245  if (finfo.Friend.StartsWith(foreignUserID.ToString()) && finfo.Friend.EndsWith(secret))
246  {
247  // great!
248  usersToBeNotified.Add(localUserID.ToString());
249  }
250  }
251  }
252  }
253 
254  // Now, let's send the notifications
255  //m_log.DebugFormat("[HGFRIENDS SERVICE]: Status notification: user has {0} local friends", usersToBeNotified.Count);
256 
257  // First, let's send notifications to local users who are online in the home grid
258  PresenceInfo[] friendSessions = m_PresenceService.GetAgents(usersToBeNotified.ToArray());
259  if (friendSessions != null && friendSessions.Length > 0)
260  {
261  PresenceInfo friendSession = null;
262  foreach (PresenceInfo pinfo in friendSessions)
263  if (pinfo.RegionID != UUID.Zero) // let's guard against traveling agents
264  {
265  friendSession = pinfo;
266  break;
267  }
268 
269  if (friendSession != null)
270  {
271  ForwardStatusNotificationToSim(friendSession.RegionID, foreignUserID, friendSession.UserID, online);
272  usersToBeNotified.Remove(friendSession.UserID.ToString());
273  UUID id;
274  if (UUID.TryParse(friendSession.UserID, out id))
275  localFriendsOnline.Add(id);
276 
277  }
278  }
279 
280 // // Lastly, let's notify the rest who may be online somewhere else
281 // foreach (string user in usersToBeNotified)
282 // {
283 // UUID id = new UUID(user);
284 // //m_UserAgentService.LocateUser(id);
285 // //etc...
286 // //if (m_TravelingAgents.ContainsKey(id) && m_TravelingAgents[id].GridExternalName != m_GridName)
287 // //{
288 // // string url = m_TravelingAgents[id].GridExternalName;
289 // // // forward
290 // //}
291 // //m_log.WarnFormat("[HGFRIENDS SERVICE]: User {0} is visiting another grid. HG Status notifications still not implemented.", user);
292 // }
293 
294  // and finally, let's send the online friends
295  if (online)
296  {
297  return localFriendsOnline;
298  }
299  else
300  return new List<UUID>();
301  }
302 
303  #endregion IHGFriendsService
304 
305  #region Aux
306 
307  private void ProcessFriendshipOffered(UUID fromID, String fromName, UUID toID, String message)
308  {
309  // Great, it's a genuine request. Let's proceed.
310  // But now we need to confirm that the requester is who he says he is
311  // before we act on the friendship request.
312 
313  if (!fromName.Contains("@"))
314  return;
315 
316  string[] parts = fromName.Split(new char[] {'@'});
317  if (parts.Length != 2)
318  return;
319 
320  string uriStr = "http://" + parts[1];
321  try
322  {
323  new Uri(uriStr);
324  }
325  catch (UriFormatException)
326  {
327  return;
328  }
329 
331  Dictionary<string, object> servers = uasConn.GetServerURLs(fromID);
332  if (!servers.ContainsKey("FriendsServerURI"))
333  return;
334 
335  HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(servers["FriendsServerURI"].ToString());
336  if (!friendsConn.ValidateFriendshipOffered(fromID, toID))
337  {
338  m_log.WarnFormat("[HGFRIENDS SERVICE]: Friendship request from {0} to {1} is invalid. Impersonations?", fromID, toID);
339  return;
340  }
341 
342  string fromUUI = Util.UniversalIdentifier(fromID, parts[0], "@" + parts[1], uriStr);
343  // OK, we're good!
344  ForwardToSim("FriendshipOffered", fromID, fromName, fromUUI, toID, message);
345  }
346 
347  private bool ForwardToSim(string op, UUID fromID, string name, String fromUUI, UUID toID, string message)
348  {
349  PresenceInfo session = null;
350  GridRegion region = null;
351  PresenceInfo[] sessions = m_PresenceService.GetAgents(new string[] { toID.ToString() });
352  if (sessions != null && sessions.Length > 0)
353  session = sessions[0];
354  if (session != null)
355  region = m_GridService.GetRegionByUUID(UUID.Zero, session.RegionID);
356 
357  switch (op)
358  {
359  case "FriendshipOffered":
360  // Let's store backwards
361  string secret = UUID.Random().ToString().Substring(0, 8);
362  m_FriendsService.StoreFriend(toID.ToString(), fromUUI + ";" + secret, 0);
363  if (m_FriendsLocalSimConnector != null) // standalone
364  {
365  GridInstantMessage im = new GridInstantMessage(null, fromID, name, toID,
366  (byte)InstantMessageDialog.FriendshipOffered, message, false, Vector3.Zero);
367  // !! HACK
368  im.imSessionID = im.fromAgentID;
369  return m_FriendsLocalSimConnector.LocalFriendshipOffered(toID, im);
370  }
371  else if (region != null) // grid
372  return m_FriendsSimConnector.FriendshipOffered(region, fromID, toID, message, name);
373  break;
374  case "ApproveFriendshipRequest":
375  if (m_FriendsLocalSimConnector != null) // standalone
376  return m_FriendsLocalSimConnector.LocalFriendshipApproved(fromID, name, toID);
377  else if (region != null) //grid
378  return m_FriendsSimConnector.FriendshipApproved(region, fromID, name, toID);
379  break;
380  }
381 
382  return false;
383  }
384 
385  protected void ForwardStatusNotificationToSim(UUID regionID, UUID foreignUserID, string user, bool online)
386  {
387  UUID userID;
388  if (UUID.TryParse(user, out userID))
389  {
390  if (m_FriendsLocalSimConnector != null)
391  {
392  m_log.DebugFormat("[HGFRIENDS SERVICE]: Local Notify, user {0} is {1}", foreignUserID, (online ? "online" : "offline"));
393  m_FriendsLocalSimConnector.StatusNotify(foreignUserID, userID, online);
394  }
395  else
396  {
397  GridRegion region = m_GridService.GetRegionByUUID(UUID.Zero /* !!! */, regionID);
398  if (region != null)
399  {
400  m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline"));
401  m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online);
402  }
403  }
404  }
405  }
406 
407  #endregion Aux
408  }
409 }
static IFriendsSimConnector m_FriendsLocalSimConnector
OpenSim.Services.Interfaces.GridRegion GridRegion
void ForwardStatusNotificationToSim(UUID regionID, UUID foreignUserID, string user, bool online)
bool DeleteFriendship(FriendInfo friend, string secret)
bool FriendshipOffered(UUID fromID, string fromName, UUID toID, string message)
bool ValidateFriendshipOffered(UUID fromID, UUID toID)
OpenSim.Services.Interfaces.FriendInfo FriendInfo
int TheirFlags
The permissions that the friend has granted to this user.
bool NewFriendship(FriendInfo friend, bool verified)
HGFriendsService(IConfigSource config, String configName, IFriendsSimConnector localSimConn)
List< UUID > StatusNotification(List< string > friends, UUID foreignUserID, bool online)