HG Friends: allow the establishment of HG friendships without requiring co-presence in the same sim. Using avatar picker, users can now search for names such as "first.last@grid.com:9000", find them, and request friendship. Friendship requests are stored if target user is offline. TESTED ON STANDALONE ONLY.

0.7.4.1
Diva Canto 2012-03-20 17:14:19 -07:00
parent d1256536b5
commit d08ad6459a
14 changed files with 2791 additions and 2191 deletions

View File

@ -1927,11 +1927,12 @@ namespace OpenSim.Framework
#region Universal User Identifiers #region Universal User Identifiers
/// <summary> /// <summary>
/// </summary> /// </summary>
/// <param name="value">uuid[;endpoint[;name]]</param> /// <param name="value">uuid[;endpoint[;first last[;secret]]]</param>
/// <param name="uuid"></param> /// <param name="uuid">the uuid part</param>
/// <param name="url"></param> /// <param name="url">the endpoint part (e.g. http://foo.com)</param>
/// <param name="firstname"></param> /// <param name="firstname">the first name part (e.g. Test)</param>
/// <param name="lastname"></param> /// <param name="lastname">the last name part (e.g User)</param>
/// <param name="secret">the secret part</param>
public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret)
{ {
uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; secret = string.Empty; uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; secret = string.Empty;
@ -1960,31 +1961,64 @@ namespace OpenSim.Framework
} }
/// <summary> /// <summary>
/// /// Produces a universal (HG) system-facing identifier given the information
/// </summary> /// </summary>
/// <param name="acircuit"></param> /// <param name="acircuit"></param>
/// <returns>uuid[;endpoint[;name]]</returns> /// <returns>uuid[;homeURI[;first last]]</returns>
public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit) public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit)
{ {
if (acircuit.ServiceURLs.ContainsKey("HomeURI")) if (acircuit.ServiceURLs.ContainsKey("HomeURI"))
{ return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString());
string agentsURI = acircuit.ServiceURLs["HomeURI"].ToString();
if (!agentsURI.EndsWith("/"))
agentsURI += "/";
// This is ugly, but there's no other way, given that the name is changed
// in the agent circuit data for foreigners
if (acircuit.lastname.Contains("@"))
{
string[] parts = acircuit.firstname.Split(new char[] { '.' });
if (parts.Length == 2)
return acircuit.AgentID.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1];
}
return acircuit.AgentID.ToString() + ";" + agentsURI + ";" + acircuit.firstname + " " + acircuit.lastname;
}
else else
return acircuit.AgentID.ToString(); return acircuit.AgentID.ToString();
} }
/// <summary>
/// Produces a universal (HG) system-facing identifier given the information
/// </summary>
/// <param name="id">UUID of the user</param>
/// <param name="firstName">first name (e.g Test)</param>
/// <param name="lastName">last name (e.g. User)</param>
/// <param name="homeURI">homeURI (e.g. http://foo.com)</param>
/// <returns>a string of the form uuid[;homeURI[;first last]]</returns>
public static string UniversalIdentifier(UUID id, String firstName, String lastName, String homeURI)
{
string agentsURI = homeURI;
if (!agentsURI.EndsWith("/"))
agentsURI += "/";
// This is ugly, but there's no other way, given that the name is changed
// in the agent circuit data for foreigners
if (lastName.Contains("@"))
{
string[] parts = firstName.Split(new char[] { '.' });
if (parts.Length == 2)
return id.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1];
}
return id.ToString() + ";" + agentsURI + ";" + firstName + " " + lastName;
}
/// <summary>
/// Produces a universal (HG) user-facing name given the information
/// </summary>
/// <param name="firstName"></param>
/// <param name="lastName"></param>
/// <param name="homeURI"></param>
/// <returns>string of the form first.last @foo.com or first last</returns>
public static string UniversalName(String firstName, String lastName, String homeURI)
{
Uri uri = null;
try
{
uri = new Uri(homeURI);
}
catch (UriFormatException)
{
return firstName + " " + lastName;
}
return firstName + "." + lastName + " " + "@" + uri.Authority;
}
#endregion #endregion
} }
} }

View File

@ -550,7 +550,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
UUID principalID = new UUID(im.fromAgentID); UUID principalID = new UUID(im.fromAgentID);
UUID friendID = new UUID(im.toAgentID); UUID friendID = new UUID(im.toAgentID);
m_log.DebugFormat("[FRIENDS]: {0} ({1}) offered friendship to {2}", principalID, im.fromAgentName, friendID); m_log.DebugFormat("[FRIENDS]: {0} ({1}) offered friendship to {2} ({3})", principalID, client.FirstName + client.LastName, friendID, im.fromAgentName);
// Check that the friendship doesn't exist yet
FriendInfo[] finfos = GetFriends(principalID);
if (finfos != null)
{
FriendInfo f = GetFriend(finfos, friendID);
if (f != null)
{
client.SendAgentAlertMessage("This person is already your friend. Please delete it first if you want to reestablish the friendship.", false);
return;
}
}
// This user wants to be friends with the other user. // This user wants to be friends with the other user.
// Let's add the relation backwards, in case the other is not online // Let's add the relation backwards, in case the other is not online
@ -561,7 +573,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
} }
} }
private void ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im) protected virtual bool ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im)
{ {
// !!!!!!!! This is a hack so that we don't have to keep state (transactionID/imSessionID) // !!!!!!!! This is a hack so that we don't have to keep state (transactionID/imSessionID)
// We stick this agent's ID as imSession, so that it's directly available on the receiving end // We stick this agent's ID as imSession, so that it's directly available on the receiving end
@ -570,7 +582,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
// Try the local sim // Try the local sim
if (LocalFriendshipOffered(friendID, im)) if (LocalFriendshipOffered(friendID, im))
return; {
m_log.DebugFormat("[XXX]: LocalFriendshipOffered successes");
return true;
}
// The prospective friend is not here [as root]. Let's forward. // The prospective friend is not here [as root]. Let's forward.
PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() });
@ -581,9 +596,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
{ {
GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID);
m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message); m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message);
return true;
} }
} }
// If the prospective friend is not online, he'll get the message upon login. // If the prospective friend is not online, he'll get the message upon login.
return false;
} }
protected virtual string GetFriendshipRequesterName(UUID agentID) protected virtual string GetFriendshipRequesterName(UUID agentID)
@ -592,7 +609,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName; return (account == null) ? "Unknown" : account.FirstName + " " + account.LastName;
} }
private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders) protected virtual void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders)
{ {
m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", client.AgentId, friendID); m_log.DebugFormat("[FRIENDS]: {0} accepted friendship from {1}", client.AgentId, friendID);
@ -603,7 +620,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
{ {
StoreFriendships(client.AgentId, friendID); StoreFriendships(client.AgentId, friendID);
// Update the local cache // Update the local cache.
RecacheFriends(client); RecacheFriends(client);
// //
@ -756,7 +773,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
#region Local #region Local
public bool LocalFriendshipOffered(UUID toID, GridInstantMessage im) public virtual bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
{ {
IClientAPI friendClient = LocateClientObject(toID); IClientAPI friendClient = LocateClientObject(toID);
if (friendClient != null) if (friendClient != null)
@ -912,7 +929,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
return FriendsService.GetFriends(client.AgentId); return FriendsService.GetFriends(client.AgentId);
} }
private void RecacheFriends(IClientAPI client) protected void RecacheFriends(IClientAPI client)
{ {
// FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event // FIXME: Ideally, we want to avoid doing this here since it sits the EventManager.OnMakeRootAgent event
// is on the critical path for transferring an avatar from one region to another. // is on the critical path for transferring an avatar from one region to another.

View File

@ -61,6 +61,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
} }
} }
protected HGFriendsServicesConnector m_HGFriendsConnector = new HGFriendsServicesConnector();
#region ISharedRegionModule #region ISharedRegionModule
public override string Name public override string Name
{ {
@ -94,6 +96,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
#endregion #endregion
protected override void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders)
{
// Update the local cache. Yes, we need to do it right here
// because the HGFriendsService placed something on the DB
// from under the sim
base.OnApproveFriendRequest(client, agentID, friendID, callingCardFolders);
}
protected override bool CacheFriends(IClientAPI client) protected override bool CacheFriends(IClientAPI client)
{ {
// m_log.DebugFormat("[HGFRIENDS MODULE]: Entered CacheFriends for {0}", client.Name); // m_log.DebugFormat("[HGFRIENDS MODULE]: Entered CacheFriends for {0}", client.Name);
@ -183,91 +193,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
// m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetOnlineFriends for {0}", userID); // m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetOnlineFriends for {0}", userID);
} }
//protected override void GetOnlineFriends(UUID userID, List<string> friendList, /*collector*/ List<UUID> online)
//{
// // Let's single out the UUIs
// List<string> localFriends = new List<string>();
// List<string> foreignFriends = new List<string>();
// string tmp = string.Empty;
// foreach (string s in friendList)
// {
// UUID id;
// if (UUID.TryParse(s, out id))
// localFriends.Add(s);
// else if (Util.ParseUniversalUserIdentifier(s, out id, out tmp, out tmp, out tmp, out tmp))
// {
// foreignFriends.Add(s);
// // add it here too, who knows maybe the foreign friends happens to be on this grid
// localFriends.Add(id.ToString());
// }
// }
// // OK, see who's present on this grid
// List<string> toBeRemoved = new List<string>();
// PresenceInfo[] presence = PresenceService.GetAgents(localFriends.ToArray());
// foreach (PresenceInfo pi in presence)
// {
// UUID presenceID;
// if (UUID.TryParse(pi.UserID, out presenceID))
// {
// online.Add(presenceID);
// foreach (string s in foreignFriends)
// if (s.StartsWith(pi.UserID))
// toBeRemoved.Add(s);
// }
// }
// foreach (string s in toBeRemoved)
// foreignFriends.Remove(s);
// // OK, let's send this up the stack, and leave a closure here
// // collecting online friends in other grids
// Util.FireAndForget(delegate { CollectOnlineFriendsElsewhere(userID, foreignFriends); });
//}
//private void CollectOnlineFriendsElsewhere(UUID userID, List<string> foreignFriends)
//{
// // let's divide the friends on a per-domain basis
// Dictionary<string, List<string>> friendsPerDomain = new Dictionary<string, List<string>>();
// foreach (string friend in foreignFriends)
// {
// UUID friendID;
// if (!UUID.TryParse(friend, out friendID))
// {
// // it's a foreign friend
// string url = string.Empty, tmp = string.Empty;
// if (Util.ParseUniversalUserIdentifier(friend, out friendID, out url, out tmp, out tmp, out tmp))
// {
// if (!friendsPerDomain.ContainsKey(url))
// friendsPerDomain[url] = new List<string>();
// friendsPerDomain[url].Add(friend);
// }
// }
// }
// // Now, call those worlds
// foreach (KeyValuePair<string, List<string>> kvp in friendsPerDomain)
// {
// List<string> ids = new List<string>();
// foreach (string f in kvp.Value)
// ids.Add(f);
// UserAgentServiceConnector uConn = new UserAgentServiceConnector(kvp.Key);
// List<UUID> online = uConn.GetOnlineFriends(userID, ids);
// // Finally send the notifications to the user
// // this whole process may take a while, so let's check at every
// // iteration that the user is still here
// IClientAPI client = LocateClientObject(userID);
// if (client != null)
// client.SendAgentOnline(online.ToArray());
// else
// break;
// }
//}
protected override void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online) protected override void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online)
{ {
// m_log.DebugFormat("[HGFRIENDS MODULE]: Entering StatusNotify for {0}", userID); // m_log.DebugFormat("[HGFRIENDS MODULE]: Entering StatusNotify for {0}", userID);
@ -335,12 +260,25 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
return true; return true;
// fid is not a UUID... // fid is not a UUID...
string url = string.Empty, tmp = string.Empty; string url = string.Empty, tmp = string.Empty, f = string.Empty, l = string.Empty;
if (Util.ParseUniversalUserIdentifier(fid, out agentID, out url, out first, out last, out tmp)) m_log.DebugFormat("[YYY]: FID {0}", fid);
if (Util.ParseUniversalUserIdentifier(fid, out agentID, out url, out f, out l, out tmp))
{ {
IUserManagement userMan = m_Scenes[0].RequestModuleInterface<IUserManagement>(); m_log.DebugFormat("[YYY]: Adding user {0} {1} {2}", f, l, url);
userMan.AddUser(agentID, first, last, url); m_uMan.AddUser(agentID, f, l, url);
string name = m_uMan.GetUserName(agentID);
string[] parts = name.Trim().Split(new char[] {' '});
if (parts.Length == 2)
{
first = parts[0];
last = parts[1];
}
else
{
first = f;
last = l;
}
return true; return true;
} }
return false; return false;
@ -348,13 +286,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
protected override string GetFriendshipRequesterName(UUID agentID) protected override string GetFriendshipRequesterName(UUID agentID)
{ {
// For the time being we assume that HG friendship requests can only happen return m_uMan.GetUserName(agentID);
// when avies are on the same region.
IClientAPI client = LocateClientObject(agentID);
if (client != null)
return client.FirstName + " " + client.LastName;
else
return base.GetFriendshipRequesterName(agentID);
} }
protected override string FriendshipMessage(string friendID) protected override string FriendshipMessage(string friendID)
@ -392,10 +324,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
AgentCircuitData agentClientCircuit = ((Scene)(client.Scene)).AuthenticateHandler.GetAgentCircuitData(client.CircuitCode); AgentCircuitData agentClientCircuit = ((Scene)(client.Scene)).AuthenticateHandler.GetAgentCircuitData(client.CircuitCode);
if (agentClientCircuit != null) if (agentClientCircuit != null)
{ {
string agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit); //[XXX] string agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit);
finfos = FriendsService.GetFriends(agentUUI); finfos = FriendsService.GetFriends(client.AgentId.ToString());
m_log.DebugFormat("[HGFRIENDS MODULE]: Fetched {0} local friends for visitor {1}", finfos.Length, agentUUI); m_log.DebugFormat("[HGFRIENDS MODULE]: Fetched {0} local friends for visitor {1}", finfos.Length, client.AgentId.ToString());
} }
// m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetFriendsFromService for {0}", client.Name); // m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetFriendsFromService for {0}", client.Name);
@ -454,16 +386,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
friendIsLocal = UserManagementModule.IsLocalGridUser(friendID); friendIsLocal = UserManagementModule.IsLocalGridUser(friendID);
} }
// Are they both local users? // Is the requester a local user?
if (agentIsLocal && friendIsLocal) if (agentIsLocal)
{ {
// local grid users // local grid users
m_log.DebugFormat("[HGFRIENDS MODULE]: Users are both local"); m_log.DebugFormat("[HGFRIENDS MODULE]: Friendship requester is local. Storing backwards.");
base.StoreBackwards(friendID, agentID); base.StoreBackwards(friendID, agentID);
return; return;
} }
// no provision for this temporary friendship state // no provision for this temporary friendship state when user is not local
//FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0); //FriendsService.StoreFriend(friendID.ToString(), agentID.ToString(), 0);
} }
@ -501,12 +434,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
agentClientCircuit = ((Scene)(agentClient.Scene)).AuthenticateHandler.GetAgentCircuitData(agentClient.CircuitCode); agentClientCircuit = ((Scene)(agentClient.Scene)).AuthenticateHandler.GetAgentCircuitData(agentClient.CircuitCode);
agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit); agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit);
agentFriendService = agentClientCircuit.ServiceURLs["FriendsServerURI"].ToString(); agentFriendService = agentClientCircuit.ServiceURLs["FriendsServerURI"].ToString();
RecacheFriends(agentClient);
} }
if (friendClient != null) if (friendClient != null)
{ {
friendClientCircuit = ((Scene)(friendClient.Scene)).AuthenticateHandler.GetAgentCircuitData(friendClient.CircuitCode); friendClientCircuit = ((Scene)(friendClient.Scene)).AuthenticateHandler.GetAgentCircuitData(friendClient.CircuitCode);
friendUUI = Util.ProduceUserUniversalIdentifier(friendClientCircuit); friendUUI = Util.ProduceUserUniversalIdentifier(friendClientCircuit);
friendFriendService = friendClientCircuit.ServiceURLs["FriendsServerURI"].ToString(); friendFriendService = friendClientCircuit.ServiceURLs["FriendsServerURI"].ToString();
RecacheFriends(friendClient);
} }
m_log.DebugFormat("[HGFRIENDS MODULE] HG Friendship! thisUUI={0}; friendUUI={1}; foreignThisFriendService={2}; foreignFriendFriendService={3}", m_log.DebugFormat("[HGFRIENDS MODULE] HG Friendship! thisUUI={0}; friendUUI={1}; foreignThisFriendService={2}; foreignFriendFriendService={3}",
@ -515,14 +450,18 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
// Generate a random 8-character hex number that will sign this friendship // Generate a random 8-character hex number that will sign this friendship
string secret = UUID.Random().ToString().Substring(0, 8); string secret = UUID.Random().ToString().Substring(0, 8);
string theFriendUUID = friendUUI + ";" + secret;
string agentUUID = agentUUI + ";" + secret;
if (agentIsLocal) // agent is local, 'friend' is foreigner if (agentIsLocal) // agent is local, 'friend' is foreigner
{ {
// This may happen when the agent returned home, in which case the friend is not there // This may happen when the agent returned home, in which case the friend is not there
// We need to look for its information in the friends list itself // We need to look for its information in the friends list itself
FriendInfo[] finfos = null;
bool confirming = false; bool confirming = false;
if (friendUUI == string.Empty) if (friendUUI == string.Empty)
{ {
FriendInfo[] finfos = GetFriends(agentID); finfos = GetFriends(agentID);
foreach (FriendInfo finfo in finfos) foreach (FriendInfo finfo in finfos)
{ {
if (finfo.TheirFlags == -1) if (finfo.TheirFlags == -1)
@ -530,29 +469,57 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
if (finfo.Friend.StartsWith(friendID.ToString())) if (finfo.Friend.StartsWith(friendID.ToString()))
{ {
friendUUI = finfo.Friend; friendUUI = finfo.Friend;
theFriendUUID = friendUUI;
UUID utmp = UUID.Zero; String url = String.Empty; String first = String.Empty, last = String.Empty, tmp = String.Empty;
// If it's confirming the friendship, we already have the full UUI with the secret
if (Util.ParseUniversalUserIdentifier(theFriendUUID, out utmp, out url, out first, out last, out secret))
{
agentUUID = agentUUI + ";" + secret;
m_uMan.AddUser(utmp, first, last, url);
}
confirming = true; confirming = true;
break;
} }
} }
} }
if (!confirming)
{
friendUUI = m_uMan.GetUserUUI(friendID);
theFriendUUID = friendUUI + ";" + secret;
}
friendFriendService = m_uMan.GetUserServerURL(friendID, "FriendsServerURI");
// m_log.DebugFormat("[HGFRIENDS MODULE] HG Friendship! thisUUI={0}; friendUUI={1}; foreignThisFriendService={2}; foreignFriendFriendService={3}",
// agentUUI, friendUUI, agentFriendService, friendFriendService);
} }
// If it's confirming the friendship, we already have the full friendUUI with the secret // Delete any previous friendship relations
string theFriendUUID = confirming ? friendUUI : friendUUI + ";" + secret; DeletePreviousRelations(agentID, friendID);
// store in the local friends service a reference to the foreign friend // store in the local friends service a reference to the foreign friend
FriendsService.StoreFriend(agentID.ToString(), theFriendUUID, 1); FriendsService.StoreFriend(agentID.ToString(), theFriendUUID, 1);
// and also the converse // and also the converse
FriendsService.StoreFriend(theFriendUUID, agentID.ToString(), 1); FriendsService.StoreFriend(theFriendUUID, agentID.ToString(), 1);
if (!confirming && friendClientCircuit != null) //if (!confirming)
{ //{
// store in the foreign friends service a reference to the local agent // store in the foreign friends service a reference to the local agent
HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(friendFriendService, friendClientCircuit.SessionID, friendClientCircuit.ServiceSessionID); HGFriendsServicesConnector friendsConn = null;
friendsConn.NewFriendship(friendID, agentUUI + ";" + secret); if (friendClientCircuit != null) // the friend is here, validate session
} friendsConn = new HGFriendsServicesConnector(friendFriendService, friendClientCircuit.SessionID, friendClientCircuit.ServiceSessionID);
else // the friend is not here, he initiated the request in his home world
friendsConn = new HGFriendsServicesConnector(friendFriendService);
friendsConn.NewFriendship(friendID, agentUUID);
//}
} }
else if (friendIsLocal) // 'friend' is local, agent is foreigner else if (friendIsLocal) // 'friend' is local, agent is foreigner
{ {
// Delete any previous friendship relations
DeletePreviousRelations(agentID, friendID);
// store in the local friends service a reference to the foreign agent // store in the local friends service a reference to the foreign agent
FriendsService.StoreFriend(friendID.ToString(), agentUUI + ";" + secret, 1); FriendsService.StoreFriend(friendID.ToString(), agentUUI + ";" + secret, 1);
// and also the converse // and also the converse
@ -582,6 +549,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
// my brain hurts now // my brain hurts now
} }
private void DeletePreviousRelations(UUID a1, UUID a2)
{
// Delete any previous friendship relations
FriendInfo[] finfos = null;
FriendInfo f = null;
finfos = GetFriends(a1);
if (finfos != null)
{
f = GetFriend(finfos, a2);
if (f != null)
{
FriendsService.Delete(a1, f.Friend);
// and also the converse
FriendsService.Delete(f.Friend, a1.ToString());
}
}
finfos = GetFriends(a2);
if (finfos != null)
{
f = GetFriend(finfos, a1);
if (f != null)
{
FriendsService.Delete(a2, f.Friend);
// and also the converse
FriendsService.Delete(f.Friend, a2.ToString());
}
}
}
protected override bool DeleteFriendship(UUID agentID, UUID exfriendID) protected override bool DeleteFriendship(UUID agentID, UUID exfriendID)
{ {
Boolean agentIsLocal = true; Boolean agentIsLocal = true;
@ -684,5 +681,74 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
friendConn.DeleteFriendship(foreignUser, localUser, secret); friendConn.DeleteFriendship(foreignUser, localUser, secret);
} }
} }
protected override bool ForwardFriendshipOffer(UUID agentID, UUID friendID, GridInstantMessage im)
{
if (base.ForwardFriendshipOffer(agentID, friendID, im))
return true;
// OK, that didn't work, so let's try to find this user somewhere
if (!m_uMan.IsLocalGridUser(friendID))
{
string friendsURL = m_uMan.GetUserServerURL(friendID, "FriendsServerURI");
if (friendsURL != string.Empty)
{
m_log.DebugFormat("[HGFRIENDS MODULE]: Forwading friendship from {0} to {1} @ {2}", agentID, friendID, friendsURL);
GridRegion region = new GridRegion();
region.ServerURI = friendsURL;
string name = im.fromAgentName;
if (m_uMan.IsLocalGridUser(agentID))
{
IClientAPI agentClient = LocateClientObject(agentID);
AgentCircuitData agentClientCircuit = ((Scene)(agentClient.Scene)).AuthenticateHandler.GetAgentCircuitData(agentClient.CircuitCode);
string agentHomeService = string.Empty;
try
{
agentHomeService = agentClientCircuit.ServiceURLs["HomeURI"].ToString();
string lastname = "@" + new Uri(agentHomeService).Authority;
string firstname = im.fromAgentName.Replace(" ", ".");
name = firstname + lastname;
}
catch (KeyNotFoundException)
{
m_log.DebugFormat("[HGFRIENDS MODULE]: Key HomeURI not found for user {0}", agentID);
return false;
}
catch (NullReferenceException)
{
m_log.DebugFormat("[HGFRIENDS MODULE]: Null HomeUri for local user {0}", agentID);
return false;
}
catch (UriFormatException)
{
m_log.DebugFormat("[HGFRIENDS MODULE]: Malformed HomeUri {0} for local user {1}", agentHomeService, agentID);
return false;
}
}
m_HGFriendsConnector.FriendshipOffered(region, agentID, friendID, im.message, name);
return true;
}
}
return false;
}
public override bool LocalFriendshipOffered(UUID toID, GridInstantMessage im)
{
if (base.LocalFriendshipOffered(toID, im))
{
if (im.fromAgentName.Contains("@"))
{
string[] parts = im.fromAgentName.Split(new char[] { '@' });
if (parts.Length == 2)
m_uMan.AddUser(new UUID(im.fromAgentID), parts[0], "http://" + parts[1]);
}
return true;
}
return false;
}
} }
} }

View File

@ -71,43 +71,52 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
protected override void AddAdditionalUsers(UUID avatarID, string query, List<UserData> users) protected override void AddAdditionalUsers(UUID avatarID, string query, List<UserData> users)
{ {
string[] words = query.Split(new char[] { ' ' }); if (query.Contains("@")) // First.Last@foo.com, maybe?
for (int i = 0; i < words.Length; i++)
{ {
if (words[i].Length < 3) string[] words = query.Split(new char[] { '@' });
if (words.Length != 2)
{ {
if (i != words.Length - 1) m_log.DebugFormat("[USER MANAGEMENT MODULE]: Malformed address {0}", query);
Array.Copy(words, i + 1, words, i, words.Length - i - 1); return;
Array.Resize(ref words, words.Length - 1);
} }
}
if (words.Length == 0 || words.Length > 2) words[0] = words[0].Trim(); // it has at least 1
return; words[1] = words[1].Trim();
if (words.Length == 2) // First.Last @foo.com, maybe? if (words[0] == String.Empty) // query was @foo.com?
{ {
bool found = false; foreach (UserData d in m_UserCache.Values)
{
if (d.LastName.ToLower().StartsWith("@" + words[1].ToLower()))
users.Add(d);
}
// We're done
return;
}
// words.Length == 2 and words[0] != string.empty
// first.last@foo.com ?
foreach (UserData d in m_UserCache.Values) foreach (UserData d in m_UserCache.Values)
{ {
if (d.LastName.StartsWith("@") && if (d.LastName.StartsWith("@") &&
d.FirstName.ToLower().Equals(words[0].ToLower()) && d.FirstName.ToLower().Equals(words[0].ToLower()) &&
d.LastName.ToLower().Equals(words[1].ToLower())) d.LastName.ToLower().Equals("@" + words[1].ToLower()))
{ {
users.Add(d); users.Add(d);
found = true; // It's cached. We're done
break; return;
} }
} }
if (!found && words[1].StartsWith("@") && words[0].Contains(".")) // This is it! Let's ask the other world // This is it! Let's ask the other world
if (words[0].Contains("."))
{ {
string[] names = words[0].Split(new char[] { '.' }); string[] names = words[0].Split(new char[] { '.' });
if (names.Length >= 2) if (names.Length >= 2)
{ {
string uriStr = "http://" + words[1].Substring(1); // remove the @ string uriStr = "http://" + words[1];
// Let's check that the last name is a valid address // Let's check that the last name is a valid address
try try
{ {
@ -115,6 +124,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
} }
catch (UriFormatException) catch (UriFormatException)
{ {
m_log.DebugFormat("[USER MANAGEMENT MODULE]: Malformed address {0}", uriStr);
return; return;
} }
@ -125,26 +135,26 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
UserData ud = new UserData(); UserData ud = new UserData();
ud.Id = userID; ud.Id = userID;
ud.FirstName = words[0]; ud.FirstName = words[0];
ud.LastName = words[1]; ud.LastName = "@" + words[1];
users.Add(ud); users.Add(ud);
AddUser(userID, ud.FirstName, ud.LastName, uriStr); AddUser(userID, names[0], names[1], uriStr);
m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0} {1} found", words[0], words[1]); m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0}@{1} found", words[0], words[1]);
} }
else else
m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0} {1} not found", words[0], words[1]); m_log.DebugFormat("[USER MANAGEMENT MODULE]: User {0}@{1} not found", words[0], words[1]);
} }
} }
} }
else //else
{ //{
foreach (UserData d in m_UserCache.Values) // foreach (UserData d in m_UserCache.Values)
{ // {
if (d.LastName.StartsWith("@") && // if (d.LastName.StartsWith("@") &&
(d.FirstName.ToLower().StartsWith(query.ToLower()) || // (d.FirstName.ToLower().StartsWith(query.ToLower()) ||
d.LastName.ToLower().StartsWith(query.ToLower()))) // d.LastName.ToLower().StartsWith(query.ToLower())))
users.Add(d); // users.Add(d);
} // }
} //}
} }
} }

View File

@ -299,7 +299,6 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
public string GetUserName(UUID uuid) public string GetUserName(UUID uuid)
{ {
//m_log.DebugFormat("[XXX] GetUserName {0}", uuid);
string[] names = GetUserNames(uuid); string[] names = GetUserNames(uuid);
if (names.Length == 2) if (names.Length == 2)
{ {
@ -340,9 +339,9 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
if (userdata.HomeURL != null && userdata.HomeURL != string.Empty) if (userdata.HomeURL != null && userdata.HomeURL != string.Empty)
{ {
m_log.DebugFormat( //m_log.DebugFormat(
"[USER MANAGEMENT MODULE]: Did not find url type {0} so requesting urls from '{1}' for {2}", // "[USER MANAGEMENT MODULE]: Did not find url type {0} so requesting urls from '{1}' for {2}",
serverType, userdata.HomeURL, userID); // serverType, userdata.HomeURL, userID);
UserAgentServiceConnector uConn = new UserAgentServiceConnector(userdata.HomeURL); UserAgentServiceConnector uConn = new UserAgentServiceConnector(userdata.HomeURL);
userdata.ServerURLs = uConn.GetServerURLs(userID); userdata.ServerURLs = uConn.GetServerURLs(userID);
@ -401,11 +400,15 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
public void AddUser(UUID uuid, string first, string last, string homeURL) public void AddUser(UUID uuid, string first, string last, string homeURL)
{ {
// m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL);
AddUser(uuid, homeURL + ";" + first + " " + last); AddUser(uuid, homeURL + ";" + first + " " + last);
} }
public void AddUser (UUID id, string creatorData) public void AddUser (UUID id, string creatorData)
{ {
//m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, creatorData {1}", id, creatorData);
UserData oldUser; UserData oldUser;
//lock the whole block - prevent concurrent update //lock the whole block - prevent concurrent update
lock (m_UserCache) lock (m_UserCache)
@ -431,9 +434,8 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
return; return;
} }
} }
// m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, creatorData {1}", id, creatorData);
UserAccount account = m_Scenes [0].UserAccountService.GetUserAccount (m_Scenes [0].RegionInfo.ScopeID, id); UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount (m_Scenes [0].RegionInfo.ScopeID, id);
if (account != null) if (account != null)
{ {
@ -482,9 +484,9 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
lock (m_UserCache) lock (m_UserCache)
m_UserCache[user.Id] = user; m_UserCache[user.Id] = user;
// m_log.DebugFormat( //m_log.DebugFormat(
// "[USER MANAGEMENT MODULE]: Added user {0} {1} {2} {3}", // "[USER MANAGEMENT MODULE]: Added user {0} {1} {2} {3}",
// user.Id, user.FirstName, user.LastName, user.HomeURL); // user.Id, user.FirstName, user.LastName, user.HomeURL);
} }
public bool IsLocalGridUser(UUID uuid) public bool IsLocalGridUser(UUID uuid)

View File

@ -48,8 +48,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid
private static bool m_Enabled = false; private static bool m_Enabled = false;
private IConfigSource m_Config; private IConfigSource m_Config;
bool m_Registered = false; private bool m_Registered = false;
GatekeeperServiceInConnector m_HypergridHandler; private string m_LocalServiceDll = String.Empty;
private GatekeeperServiceInConnector m_HypergridHandler;
private UserAgentServerConnector m_UASHandler;
#region IRegionModule interface #region IRegionModule interface
@ -63,6 +65,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid
if (m_Enabled) if (m_Enabled)
{ {
m_log.Info("[HGGRID IN CONNECTOR]: Hypergrid Service In Connector enabled"); m_log.Info("[HGGRID IN CONNECTOR]: Hypergrid Service In Connector enabled");
IConfig fconfig = config.Configs["FriendsService"];
if (fconfig != null)
{
m_LocalServiceDll = fconfig.GetString("LocalServiceModule", m_LocalServiceDll);
if (m_LocalServiceDll == String.Empty)
m_log.WarnFormat("[HGGRID IN CONNECTOR]: Friends LocalServiceModule config missing");
}
} }
} }
@ -91,7 +100,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid
{ {
if (!m_Enabled) if (!m_Enabled)
return; return;
} }
public void RemoveRegion(Scene scene) public void RemoveRegion(Scene scene)
@ -112,14 +120,20 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid
m_log.Info("[HypergridService]: Starting..."); m_log.Info("[HypergridService]: Starting...");
ISimulationService simService = scene.RequestModuleInterface<ISimulationService>(); ISimulationService simService = scene.RequestModuleInterface<ISimulationService>();
IFriendsSimConnector friendsConn = scene.RequestModuleInterface<IFriendsSimConnector>();
Object[] args = new Object[] { m_Config };
IFriendsService friendsService = ServerUtils.LoadPlugin<IFriendsService>(m_LocalServiceDll, args);
m_HypergridHandler = new GatekeeperServiceInConnector(m_Config, MainServer.Instance, simService); m_HypergridHandler = new GatekeeperServiceInConnector(m_Config, MainServer.Instance, simService);
IFriendsSimConnector friendsConn = scene.RequestModuleInterface<IFriendsSimConnector>(); m_UASHandler = new UserAgentServerConnector(m_Config, MainServer.Instance, friendsConn);
new UserAgentServerConnector(m_Config, MainServer.Instance, friendsConn);
new HeloServiceInConnector(m_Config, MainServer.Instance, "HeloService"); new HeloServiceInConnector(m_Config, MainServer.Instance, "HeloService");
new HGFriendsServerConnector(m_Config, MainServer.Instance, "HGFriendsService");
new HGFriendsServerConnector(m_Config, MainServer.Instance, "HGFriendsService", friendsConn);
} }
scene.RegisterModuleInterface<IGatekeeperService>(m_HypergridHandler.GateKeeper); scene.RegisterModuleInterface<IGatekeeperService>(m_HypergridHandler.GateKeeper);
scene.RegisterModuleInterface<IUserAgentService>(m_UASHandler.HomeUsersService);
} }
#endregion #endregion

View File

@ -36,36 +36,42 @@ namespace OpenSim.Server.Handlers.Hypergrid
{ {
public class HGFriendsServerConnector : ServiceConnector public class HGFriendsServerConnector : ServiceConnector
{ {
private IFriendsService m_FriendsService;
private IUserAgentService m_UserAgentService; private IUserAgentService m_UserAgentService;
private IHGFriendsService m_TheService;
private string m_ConfigName = "HGFriendsService"; private string m_ConfigName = "HGFriendsService";
// Called from Robust
public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName) : public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName) :
base(config, server, configName) this(config, server, configName, null)
{
}
// Called from standalone configurations
public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName, IFriendsSimConnector localConn)
: base(config, server, configName)
{ {
if (configName != string.Empty) if (configName != string.Empty)
m_ConfigName = configName; m_ConfigName = configName;
Object[] args = new Object[] { config, m_ConfigName, localConn };
IConfig serverConfig = config.Configs[m_ConfigName]; IConfig serverConfig = config.Configs[m_ConfigName];
if (serverConfig == null) if (serverConfig == null)
throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); throw new Exception(String.Format("No section {0} in config file", m_ConfigName));
string theService = serverConfig.GetString("LocalServiceModule", string theService = serverConfig.GetString("LocalServiceModule",
String.Empty); String.Empty);
if (theService == String.Empty) if (theService == String.Empty)
throw new Exception("No LocalServiceModule in config file"); throw new Exception("No LocalServiceModule in config file");
m_TheService = ServerUtils.LoadPlugin<IHGFriendsService>(theService, args);
Object[] args = new Object[] { config };
m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(theService, args);
theService = serverConfig.GetString("UserAgentService", string.Empty); theService = serverConfig.GetString("UserAgentService", string.Empty);
if (theService == String.Empty) if (theService == String.Empty)
throw new Exception("No UserAgentService in " + m_ConfigName); throw new Exception("No UserAgentService in " + m_ConfigName);
m_UserAgentService = ServerUtils.LoadPlugin<IUserAgentService>(theService, new Object[] { config, localConn });
m_UserAgentService = ServerUtils.LoadPlugin<IUserAgentService>(theService, args); server.AddStreamHandler(new HGFriendsServerPostHandler(m_TheService, m_UserAgentService, localConn));
server.AddStreamHandler(new HGFriendsServerPostHandler(m_FriendsService, m_UserAgentService));
} }
} }
} }

View File

@ -39,6 +39,7 @@ using System.Collections.Generic;
using OpenSim.Server.Base; using OpenSim.Server.Base;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer; using OpenSim.Framework.Servers.HttpServer;
using OpenMetaverse; using OpenMetaverse;
@ -49,15 +50,22 @@ namespace OpenSim.Server.Handlers.Hypergrid
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IFriendsService m_FriendsService;
private IUserAgentService m_UserAgentService; private IUserAgentService m_UserAgentService;
private IFriendsSimConnector m_FriendsLocalSimConnector;
private IHGFriendsService m_TheService;
public HGFriendsServerPostHandler(IFriendsService service, IUserAgentService uservice) : public HGFriendsServerPostHandler(IHGFriendsService service, IUserAgentService uas, IFriendsSimConnector friendsConn) :
base("POST", "/hgfriends") base("POST", "/hgfriends")
{ {
m_FriendsService = service; m_TheService = service;
m_UserAgentService = uservice; m_UserAgentService = uas;
m_log.DebugFormat("[HGFRIENDS HANDLER]: HGFriendsServerPostHandler is On"); m_FriendsLocalSimConnector = friendsConn;
m_log.DebugFormat("[HGFRIENDS HANDLER]: HGFriendsServerPostHandler is On ({0})",
(m_FriendsLocalSimConnector == null ? "robust" : "standalone"));
if (m_TheService == null)
m_log.ErrorFormat("[HGFRIENDS HANDLER]: TheService is null!");
} }
public override byte[] Handle(string path, Stream requestData, public override byte[] Handle(string path, Stream requestData,
@ -90,6 +98,26 @@ namespace OpenSim.Server.Handlers.Hypergrid
case "deletefriendship": case "deletefriendship":
return DeleteFriendship(request); return DeleteFriendship(request);
/* Same as inter-sim */
case "friendship_offered":
return FriendshipOffered(request);
case "validate_friendship_offered":
return ValidateFriendshipOffered(request);
/*
case "friendship_approved":
return FriendshipApproved(request);
case "friendship_denied":
return FriendshipDenied(request);
case "friendship_terminated":
return FriendshipTerminated(request);
case "grant_rights":
return GrantRights(request);
*/
} }
m_log.DebugFormat("[HGFRIENDS HANDLER]: unknown method {0} request {1}", method.Length, method); m_log.DebugFormat("[HGFRIENDS HANDLER]: unknown method {0} request {1}", method.Length, method);
} }
@ -126,39 +154,20 @@ namespace OpenSim.Server.Handlers.Hypergrid
return FailureResult(); return FailureResult();
} }
FriendInfo[] friendsInfo = m_FriendsService.GetFriends(principalID); int perms = m_TheService.GetFriendPerms(principalID, friendID);
foreach (FriendInfo finfo in friendsInfo) if (perms < 0)
{ return FailureResult("Friend not found");
if (finfo.Friend.StartsWith(friendID.ToString()))
return SuccessResult(finfo.TheirFlags.ToString());
}
return FailureResult("Friend not found"); return SuccessResult(perms.ToString());
} }
byte[] NewFriendship(Dictionary<string, object> request) byte[] NewFriendship(Dictionary<string, object> request)
{ {
if (!VerifyServiceKey(request)) bool verified = VerifyServiceKey(request);
return FailureResult();
// OK, can proceed
FriendInfo friend = new FriendInfo(request); FriendInfo friend = new FriendInfo(request);
UUID friendID;
string tmp = string.Empty;
if (!Util.ParseUniversalUserIdentifier(friend.Friend, out friendID, out tmp, out tmp, out tmp, out tmp))
return FailureResult();
bool success = m_TheService.NewFriendship(friend, verified);
m_log.DebugFormat("[HGFRIENDS HANDLER]: New friendship {0} {1}", friend.PrincipalID, friend.Friend);
// If the friendship already exists, return fail
FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID);
foreach (FriendInfo finfo in finfos)
if (finfo.Friend.StartsWith(friendID.ToString()))
return FailureResult();
// the user needs to confirm when he gets home
bool success = m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 0);
if (success) if (success)
return SuccessResult(); return SuccessResult();
@ -174,25 +183,53 @@ namespace OpenSim.Server.Handlers.Hypergrid
secret = request["SECRET"].ToString(); secret = request["SECRET"].ToString();
if (secret == string.Empty) if (secret == string.Empty)
return FailureResult(); return BoolResult(false);
FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); bool success = m_TheService.DeleteFriendship(friend, secret);
foreach (FriendInfo finfo in finfos)
{
// We check the secret here
if (finfo.Friend.StartsWith(friend.Friend) && finfo.Friend.EndsWith(secret))
{
m_log.DebugFormat("[HGFRIENDS HANDLER]: Delete friendship {0} {1}", friend.PrincipalID, friend.Friend);
m_FriendsService.Delete(friend.PrincipalID, finfo.Friend);
m_FriendsService.Delete(finfo.Friend, friend.PrincipalID.ToString());
return SuccessResult(); return BoolResult(success);
}
}
return FailureResult();
} }
byte[] FriendshipOffered(Dictionary<string, object> request)
{
UUID fromID = UUID.Zero;
UUID toID = UUID.Zero;
string message = string.Empty;
string name = string.Empty;
m_log.DebugFormat("[HGFRIENDS HANDLER]: Friendship offered");
if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID"))
return BoolResult(false);
if (!UUID.TryParse(request["ToID"].ToString(), out toID))
return BoolResult(false);
message = request["Message"].ToString();
if (!UUID.TryParse(request["FromID"].ToString(), out fromID))
return BoolResult(false);
if (request.ContainsKey("FromName"))
name = request["FromName"].ToString();
bool success = m_TheService.FriendshipOffered(fromID, name, toID, message);
return BoolResult(success);
}
byte[] ValidateFriendshipOffered(Dictionary<string, object> request)
{
FriendInfo friend = new FriendInfo(request);
UUID friendID = UUID.Zero;
if (!UUID.TryParse(friend.Friend, out friendID))
return BoolResult(false);
bool success = m_TheService.ValidateFriendshipOffered(friend.PrincipalID, friendID);
return BoolResult(success);
}
#endregion #endregion
#region Misc #region Misc
@ -205,10 +242,15 @@ namespace OpenSim.Server.Handlers.Hypergrid
return false; return false;
} }
if (request["KEY"] == null || request["SESSIONID"] == null)
return false;
string serviceKey = request["KEY"].ToString(); string serviceKey = request["KEY"].ToString();
string sessionStr = request["SESSIONID"].ToString(); string sessionStr = request["SESSIONID"].ToString();
UUID sessionID; UUID sessionID;
UUID.TryParse(sessionStr, out sessionID); if (!UUID.TryParse(sessionStr, out sessionID) || serviceKey == string.Empty)
return false;
if (!m_UserAgentService.VerifyAgent(sessionID, serviceKey)) if (!m_UserAgentService.VerifyAgent(sessionID, serviceKey))
{ {
@ -256,7 +298,7 @@ namespace OpenSim.Server.Handlers.Hypergrid
doc.AppendChild(rootElement); doc.AppendChild(rootElement);
XmlElement result = doc.CreateElement("", "Result", ""); XmlElement result = doc.CreateElement("", "RESULT", "");
result.AppendChild(doc.CreateTextNode("Success")); result.AppendChild(doc.CreateTextNode("Success"));
rootElement.AppendChild(result); rootElement.AppendChild(result);
@ -289,7 +331,7 @@ namespace OpenSim.Server.Handlers.Hypergrid
doc.AppendChild(rootElement); doc.AppendChild(rootElement);
XmlElement result = doc.CreateElement("", "Result", ""); XmlElement result = doc.CreateElement("", "RESULT", "");
result.AppendChild(doc.CreateTextNode("Failure")); result.AppendChild(doc.CreateTextNode("Failure"));
rootElement.AppendChild(result); rootElement.AppendChild(result);
@ -302,6 +344,28 @@ namespace OpenSim.Server.Handlers.Hypergrid
return DocToBytes(doc); return DocToBytes(doc);
} }
private byte[] BoolResult(bool value)
{
XmlDocument doc = new XmlDocument();
XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration,
"", "");
doc.AppendChild(xmlnode);
XmlElement rootElement = doc.CreateElement("", "ServerResponse",
"");
doc.AppendChild(rootElement);
XmlElement result = doc.CreateElement("", "RESULT", "");
result.AppendChild(doc.CreateTextNode(value.ToString()));
rootElement.AppendChild(result);
return DocToBytes(doc);
}
private byte[] DocToBytes(XmlDocument doc) private byte[] DocToBytes(XmlDocument doc)
{ {
MemoryStream ms = new MemoryStream(); MemoryStream ms = new MemoryStream();
@ -313,6 +377,7 @@ namespace OpenSim.Server.Handlers.Hypergrid
return ms.ToArray(); return ms.ToArray();
} }
#endregion #endregion
} }
} }

View File

@ -52,6 +52,11 @@ namespace OpenSim.Server.Handlers.Hypergrid
// MethodBase.GetCurrentMethod().DeclaringType); // MethodBase.GetCurrentMethod().DeclaringType);
private IUserAgentService m_HomeUsersService; private IUserAgentService m_HomeUsersService;
public IUserAgentService HomeUsersService
{
get { return m_HomeUsersService; }
}
private string[] m_AuthorizedCallers; private string[] m_AuthorizedCallers;
private bool m_VerifyCallers = false; private bool m_VerifyCallers = false;

View File

@ -43,7 +43,17 @@ namespace OpenSim.Services.Connectors.Friends
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected virtual string ServicePath()
{
return "friends";
}
public bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message) public bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message)
{
return FriendshipOffered(region, userID, friendID, message, String.Empty);
}
public virtual bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message, string userName)
{ {
Dictionary<string, object> sendData = new Dictionary<string, object>(); Dictionary<string, object> sendData = new Dictionary<string, object>();
//sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString();
@ -53,9 +63,10 @@ namespace OpenSim.Services.Connectors.Friends
sendData["FromID"] = userID.ToString(); sendData["FromID"] = userID.ToString();
sendData["ToID"] = friendID.ToString(); sendData["ToID"] = friendID.ToString();
sendData["Message"] = message; sendData["Message"] = message;
if (userName != String.Empty)
sendData["FromName"] = userName;
return Call(region, sendData); return Call(region, sendData);
} }
public bool FriendshipApproved(GridRegion region, UUID userID, string userName, UUID friendID) public bool FriendshipApproved(GridRegion region, UUID userID, string userName, UUID friendID)
@ -138,8 +149,11 @@ namespace OpenSim.Services.Connectors.Friends
if (region == null) if (region == null)
return false; return false;
m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: region: {0}", region.ExternalHostName + ":" + region.HttpPort); string path = ServicePath();
string uri = "http://" + region.ExternalHostName + ":" + region.HttpPort + "/friends"; if (!region.ServerURI.EndsWith("/"))
path = "/" + path;
string uri = region.ServerURI + path;
m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri);
try try
{ {

View File

@ -40,7 +40,7 @@ using OpenMetaverse;
namespace OpenSim.Services.Connectors.Hypergrid namespace OpenSim.Services.Connectors.Hypergrid
{ {
public class HGFriendsServicesConnector public class HGFriendsServicesConnector : FriendsSimConnector
{ {
private static readonly ILog m_log = private static readonly ILog m_log =
LogManager.GetLogger( LogManager.GetLogger(
@ -66,6 +66,11 @@ namespace OpenSim.Services.Connectors.Hypergrid
m_SessionID = sessionID; m_SessionID = sessionID;
} }
protected override string ServicePath()
{
return "hgfriends";
}
#region IFriendsService #region IFriendsService
public uint GetFriendPerms(UUID PrincipalID, UUID friendID) public uint GetFriendPerms(UUID PrincipalID, UUID friendID)
@ -187,23 +192,69 @@ namespace OpenSim.Services.Connectors.Hypergrid
{ {
Dictionary<string, object> replyData = ServerUtils.ParseXmlResponse(reply); Dictionary<string, object> replyData = ServerUtils.ParseXmlResponse(reply);
if ((replyData != null) && replyData.ContainsKey("Result") && (replyData["Result"] != null)) if (replyData.ContainsKey("RESULT"))
{ {
bool success = false; if (replyData["RESULT"].ToString().ToLower() == "true")
Boolean.TryParse(replyData["Result"].ToString(), out success); return true;
return success; else
return false;
} }
else else
m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Delete {0} {1} received null response", m_log.DebugFormat("[HGFRIENDS CONNECTOR]: reply data does not contain result field");
PrincipalID, Friend);
} }
else else
m_log.DebugFormat("[HGFRIENDS CONNECTOR]: DeleteFriend received null reply"); m_log.DebugFormat("[HGFRIENDS CONNECTOR]: received empty reply");
return false; return false;
} }
public bool ValidateFriendshipOffered(UUID fromID, UUID toID)
{
FriendInfo finfo = new FriendInfo();
finfo.PrincipalID = fromID;
finfo.Friend = toID.ToString();
Dictionary<string, object> sendData = finfo.ToKeyValuePairs();
sendData["METHOD"] = "validate_friendship_offered";
string reply = string.Empty;
string uri = m_ServerURI + "/hgfriends";
try
{
reply = SynchronousRestFormsRequester.MakeRequest("POST",
uri,
ServerUtils.BuildQueryString(sendData));
}
catch (Exception e)
{
m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message);
return false;
}
if (reply != string.Empty)
{
Dictionary<string, object> replyData = ServerUtils.ParseXmlResponse(reply);
if (replyData.ContainsKey("RESULT"))
{
if (replyData["RESULT"].ToString().ToLower() == "true")
return true;
else
return false;
}
else
m_log.DebugFormat("[HGFRIENDS CONNECTOR]: reply data does not contain result field");
}
else
m_log.DebugFormat("[HGFRIENDS CONNECTOR]: received empty reply");
return false;
}
#endregion #endregion
} }
} }

View File

@ -0,0 +1,301 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using OpenSim.Framework;
using OpenSim.Services.Connectors.Friends;
using OpenSim.Services.Connectors.Hypergrid;
using OpenSim.Services.Interfaces;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
using OpenSim.Server.Base;
using FriendInfo = OpenSim.Services.Interfaces.FriendInfo;
using OpenMetaverse;
using log4net;
using Nini.Config;
namespace OpenSim.Services.HypergridService
{
/// <summary>
/// W2W social networking
/// </summary>
public class HGFriendsService : IHGFriendsService
{
private static readonly ILog m_log =
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
static bool m_Initialized = false;
protected static IGridUserService m_GridUserService;
protected static IGridService m_GridService;
protected static IGatekeeperService m_GatekeeperService;
protected static IFriendsService m_FriendsService;
protected static IPresenceService m_PresenceService;
protected static IUserAccountService m_UserAccountService;
protected static IFriendsSimConnector m_FriendsLocalSimConnector; // standalone, points to HGFriendsModule
protected static FriendsSimConnector m_FriendsSimConnector; // grid
private static string m_ConfigName = "HGFriendsService";
public HGFriendsService(IConfigSource config, String configName, IFriendsSimConnector localSimConn)
{
if (m_FriendsLocalSimConnector == null)
m_FriendsLocalSimConnector = localSimConn;
if (!m_Initialized)
{
m_Initialized = true;
if (configName != String.Empty)
m_ConfigName = configName;
Object[] args = new Object[] { config };
IConfig serverConfig = config.Configs[m_ConfigName];
if (serverConfig == null)
throw new Exception(String.Format("No section {0} in config file", m_ConfigName));
string theService = serverConfig.GetString("FriendsService", string.Empty);
if (theService == String.Empty)
throw new Exception("No FriendsService in config file " + m_ConfigName);
m_FriendsService = ServerUtils.LoadPlugin<IFriendsService>(theService, args);
theService = serverConfig.GetString("UserAccountService", string.Empty);
if (theService == String.Empty)
throw new Exception("No UserAccountService in " + m_ConfigName);
m_UserAccountService = ServerUtils.LoadPlugin<IUserAccountService>(theService, args);
theService = serverConfig.GetString("GridService", string.Empty);
if (theService == String.Empty)
throw new Exception("No GridService in " + m_ConfigName);
m_GridService = ServerUtils.LoadPlugin<IGridService>(theService, args);
theService = serverConfig.GetString("PresenceService", string.Empty);
if (theService == String.Empty)
throw new Exception("No PresenceService in " + m_ConfigName);
m_PresenceService = ServerUtils.LoadPlugin<IPresenceService>(theService, args);
m_FriendsSimConnector = new FriendsSimConnector();
m_log.DebugFormat("[HGFRIENDS SERVICE]: Starting...");
}
}
#region IHGFriendsService
public int GetFriendPerms(UUID userID, UUID friendID)
{
FriendInfo[] friendsInfo = m_FriendsService.GetFriends(userID);
foreach (FriendInfo finfo in friendsInfo)
{
if (finfo.Friend.StartsWith(friendID.ToString()))
return finfo.TheirFlags;
}
return -1;
}
public bool NewFriendship(FriendInfo friend, bool verified)
{
UUID friendID;
string tmp = string.Empty, url = String.Empty, first = String.Empty, last = String.Empty;
if (!Util.ParseUniversalUserIdentifier(friend.Friend, out friendID, out url, out first, out last, out tmp))
return false;
m_log.DebugFormat("[HGFRIENDS SERVICE]: New friendship {0} {1} ({2})", friend.PrincipalID, friend.Friend, verified);
// Does the friendship already exist?
FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID);
foreach (FriendInfo finfo in finfos)
{
if (finfo.Friend.StartsWith(friendID.ToString()))
return false;
}
// Verified user session. But the user needs to confirm friendship when he gets home
if (verified)
return m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 0);
// Does the reverted friendship exist? meaning that this user initiated the request
finfos = m_FriendsService.GetFriends(friendID);
bool userInitiatedOffer = false;
foreach (FriendInfo finfo in finfos)
{
if (friend.Friend.StartsWith(finfo.PrincipalID.ToString()) && finfo.Friend.StartsWith(friend.PrincipalID.ToString()) && finfo.TheirFlags == -1)
{
userInitiatedOffer = true;
// Let's delete the existing friendship relations that was stored
m_FriendsService.Delete(friendID, finfo.Friend);
break;
}
}
if (userInitiatedOffer)
{
m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 1);
m_FriendsService.StoreFriend(friend.Friend, friend.PrincipalID.ToString(), 1);
// notify the user
ForwardToSim("ApproveFriendshipRequest", friendID, Util.UniversalName(first, last, url), "", friend.PrincipalID, "");
return true;
}
return false;
}
public bool DeleteFriendship(FriendInfo friend, string secret)
{
FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID);
foreach (FriendInfo finfo in finfos)
{
// We check the secret here. Or if the friendship request was initiated here, and was declined
if (finfo.Friend.StartsWith(friend.Friend) && finfo.Friend.EndsWith(secret))
{
m_log.DebugFormat("[HGFRIENDS SERVICE]: Delete friendship {0} {1}", friend.PrincipalID, friend.Friend);
m_FriendsService.Delete(friend.PrincipalID, finfo.Friend);
m_FriendsService.Delete(finfo.Friend, friend.PrincipalID.ToString());
return true;
}
}
return false;
}
public bool FriendshipOffered(UUID fromID, string fromName, UUID toID, string message)
{
UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, toID);
if (account == null)
return false;
// OK, we have that user here.
// So let's send back the call, but start a thread to continue
// with the verification and the actual action.
Util.FireAndForget(delegate { ProcessFriendshipOffered(fromID, fromName, toID, message); });
return true;
}
public bool ValidateFriendshipOffered(UUID fromID, UUID toID)
{
FriendInfo[] finfos = m_FriendsService.GetFriends(toID.ToString());
foreach (FriendInfo fi in finfos)
{
if (fi.Friend.StartsWith(fromID.ToString()) && fi.TheirFlags == -1)
return true;
}
return false;
}
#endregion IHGFriendsService
#region Aux
private void ProcessFriendshipOffered(UUID fromID, String fromName, UUID toID, String message)
{
// Great, it's a genuine request. Let's proceed.
// But now we need to confirm that the requester is who he says he is
// before we act on the friendship request.
if (!fromName.Contains("@"))
return;
string[] parts = fromName.Split(new char[] {'@'});
if (parts.Length != 2)
return;
string uriStr = "http://" + parts[1];
try
{
new Uri(uriStr);
}
catch (UriFormatException)
{
return;
}
UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr);
Dictionary<string, object> servers = uasConn.GetServerURLs(fromID);
if (!servers.ContainsKey("FriendsServerURI"))
return;
HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(servers["FriendsServerURI"].ToString());
if (!friendsConn.ValidateFriendshipOffered(fromID, toID))
{
m_log.WarnFormat("[HGFRIENDS SERVICE]: Friendship request from {0} to {1} is invalid. Impersonations?", fromID, toID);
return;
}
string fromUUI = Util.UniversalIdentifier(fromID, parts[0], "@" + parts[1], uriStr);
// OK, we're good!
ForwardToSim("FriendshipOffered", fromID, fromName, fromUUI, toID, message);
}
private bool ForwardToSim(string op, UUID fromID, string name, String fromUUI, UUID toID, string message)
{
PresenceInfo session = null;
GridRegion region = null;
PresenceInfo[] sessions = m_PresenceService.GetAgents(new string[] { toID.ToString() });
if (sessions != null && sessions.Length > 0)
session = sessions[0];
if (session != null)
region = m_GridService.GetRegionByUUID(UUID.Zero, session.RegionID);
switch (op)
{
case "FriendshipOffered":
// Let's store backwards
string secret = UUID.Random().ToString().Substring(0, 8);
m_FriendsService.StoreFriend(toID.ToString(), fromUUI + ";" + secret, 0);
if (m_FriendsLocalSimConnector != null) // standalone
{
GridInstantMessage im = new GridInstantMessage(null, fromID, name, toID,
(byte)InstantMessageDialog.FriendshipOffered, message, false, Vector3.Zero);
// !! HACK
im.imSessionID = im.fromAgentID;
return m_FriendsLocalSimConnector.LocalFriendshipOffered(toID, im);
}
else if (region != null) // grid
return m_FriendsSimConnector.FriendshipOffered(region, fromID, toID, message, name);
break;
case "ApproveFriendshipRequest":
if (m_FriendsLocalSimConnector != null) // standalone
return m_FriendsLocalSimConnector.LocalFriendshipApproved(fromID, name, toID);
else if (region != null) //grid
return m_FriendsSimConnector.FriendshipApproved(region, fromID, name, toID);
break;
}
return false;
}
#endregion Aux
}
}

View File

@ -81,6 +81,17 @@ namespace OpenSim.Services.Interfaces
public interface IFriendsSimConnector public interface IFriendsSimConnector
{ {
bool StatusNotify(UUID userID, UUID friendID, bool online); bool StatusNotify(UUID userID, UUID friendID, bool online);
bool LocalFriendshipOffered(UUID toID, GridInstantMessage im);
bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID);
}
public interface IHGFriendsService
{
int GetFriendPerms(UUID userID, UUID friendID);
bool NewFriendship(FriendInfo finfo, bool verified);
bool DeleteFriendship(FriendInfo finfo, string secret);
bool FriendshipOffered(UUID from, string fromName, UUID to, string message);
bool ValidateFriendshipOffered(UUID fromID, UUID toID);
} }
public interface IInstantMessageSimConnector public interface IInstantMessageSimConnector

View File

@ -159,8 +159,12 @@
UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService"
[HGFriendsService] [HGFriendsService]
LocalServiceModule = "OpenSim.Services.FriendsService.dll:FriendsService" LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGFriendsService"
UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService"
FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService"
UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService"
GridService = "OpenSim.Services.GridService.dll:GridService"
PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService"
[HGInstantMessageService] [HGInstantMessageService]
LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInstantMessageService" LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInstantMessageService"