OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
SimianProfiles.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.Collections.Specialized;
31 using System.Reflection;
32 using log4net;
33 using Mono.Addins;
34 using Nini.Config;
35 using OpenMetaverse;
36 using OpenMetaverse.StructuredData;
37 using OpenSim.Framework;
38 using OpenSim.Framework.Client;
39 using OpenSim.Region.Framework.Interfaces;
40 using OpenSim.Region.Framework.Scenes;
41 using OpenSim.Services.Interfaces;
42 
43 namespace OpenSim.Services.Connectors.SimianGrid
44 {
48  [Flags]
49  public enum ProfileFlags : uint
50  {
51  AllowPublish = 1,
52  MaturePublish = 2,
53  Identified = 4,
54  Transacted = 8,
55  Online = 16
56  }
57 
62  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianProfiles")]
64  {
65  private static readonly ILog m_log =
66  LogManager.GetLogger(
67  MethodBase.GetCurrentMethod().DeclaringType);
68 
69  private string m_serverUrl = String.Empty;
70  private bool m_Enabled = false;
71 
72  #region INonSharedRegionModule
73 
74  public Type ReplaceableInterface { get { return null; } }
75  public void RegionLoaded(Scene scene) { }
76  public void Close() { }
77 
78  public SimianProfiles() { }
79  public string Name { get { return "SimianProfiles"; } }
80 
81  public void AddRegion(Scene scene)
82  {
83  if (m_Enabled)
84  {
85  CheckEstateManager(scene);
86  scene.EventManager.OnClientConnect += ClientConnectHandler;
87  }
88  }
89 
90  public void RemoveRegion(Scene scene)
91  {
92  if (m_Enabled)
93  {
94  scene.EventManager.OnClientConnect -= ClientConnectHandler;
95  }
96  }
97 
98  #endregion INonSharedRegionModule
99 
100  public SimianProfiles(IConfigSource source)
101  {
102  Initialise(source);
103  }
104 
105  public void Initialise(IConfigSource source)
106  {
107  IConfig profileConfig = source.Configs["Profiles"];
108  if (profileConfig == null)
109  return;
110 
111  if (profileConfig.GetString("Module", String.Empty) != Name)
112  return;
113 
114  m_log.DebugFormat("[SIMIAN PROFILES] module enabled");
115  m_Enabled = true;
116 
117  IConfig gridConfig = source.Configs["UserAccountService"];
118  if (gridConfig != null)
119  {
120  string serviceUrl = gridConfig.GetString("UserAccountServerURI");
121  if (!String.IsNullOrEmpty(serviceUrl))
122  {
123  if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("="))
124  serviceUrl = serviceUrl + '/';
125  m_serverUrl = serviceUrl;
126  }
127  }
128 
129  if (String.IsNullOrEmpty(m_serverUrl))
130  m_log.Info("[SIMIAN PROFILES]: No UserAccountServerURI specified, disabling connector");
131  }
132 
133  private void ClientConnectHandler(IClientCore clientCore)
134  {
135  if (clientCore is IClientAPI)
136  {
137  IClientAPI client = (IClientAPI)clientCore;
138 
139  // Classifieds
140  client.AddGenericPacketHandler("avatarclassifiedsrequest", AvatarClassifiedsRequestHandler);
141  client.OnClassifiedInfoRequest += ClassifiedInfoRequestHandler;
142  client.OnClassifiedInfoUpdate += ClassifiedInfoUpdateHandler;
143  client.OnClassifiedDelete += ClassifiedDeleteHandler;
144 
145  // Picks
146  client.AddGenericPacketHandler("avatarpicksrequest", HandleAvatarPicksRequest);
147  client.AddGenericPacketHandler("pickinforequest", HandlePickInfoRequest);
148  client.OnPickInfoUpdate += PickInfoUpdateHandler;
149  client.OnPickDelete += PickDeleteHandler;
150 
151  // Notes
152  client.AddGenericPacketHandler("avatarnotesrequest", HandleAvatarNotesRequest);
153  client.OnAvatarNotesUpdate += AvatarNotesUpdateHandler;
154 
155  // Profiles
156  client.OnRequestAvatarProperties += RequestAvatarPropertiesHandler;
157 
158  client.OnUpdateAvatarProperties += UpdateAvatarPropertiesHandler;
159  client.OnAvatarInterestUpdate += AvatarInterestUpdateHandler;
160  client.OnUserInfoRequest += UserInfoRequestHandler;
161  client.OnUpdateUserInfo += UpdateUserInfoHandler;
162  }
163  }
164 
165  #region Classifieds
166 
167  private void AvatarClassifiedsRequestHandler(Object sender, string method, List<String> args)
168  {
169  if (!(sender is IClientAPI))
170  return;
171  IClientAPI client = (IClientAPI)sender;
172 
173  UUID targetAvatarID;
174  if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID))
175  {
176  m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method);
177  return;
178  }
179 
180  // FIXME: Query the generic key/value store for classifieds
181  client.SendAvatarClassifiedReply(targetAvatarID, new Dictionary<UUID, string>(0));
182  }
183 
184  private void ClassifiedInfoRequestHandler(UUID classifiedID, IClientAPI client)
185  {
186  // FIXME: Fetch this info
187  client.SendClassifiedInfoReply(classifiedID, UUID.Zero, 0, Utils.DateTimeToUnixTime(DateTime.UtcNow + TimeSpan.FromDays(1)),
188  0, String.Empty, String.Empty, UUID.Zero, 0, UUID.Zero, String.Empty, Vector3.Zero, String.Empty, 0, 0);
189  }
190 
191  private void ClassifiedInfoUpdateHandler(UUID classifiedID, uint category, string name, string description,
192  UUID parcelID, uint parentEstate, UUID snapshotID, Vector3 globalPos, byte classifiedFlags, int price,
193  IClientAPI client)
194  {
195  // FIXME: Save this info
196  }
197 
198  private void ClassifiedDeleteHandler(UUID classifiedID, IClientAPI client)
199  {
200  // FIXME: Delete the specified classified ad
201  }
202 
203  #endregion Classifieds
204 
205  #region Picks
206 
207  private void HandleAvatarPicksRequest(Object sender, string method, List<String> args)
208  {
209  if (!(sender is IClientAPI))
210  return;
211  IClientAPI client = (IClientAPI)sender;
212 
213  UUID targetAvatarID;
214  if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID))
215  {
216  m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method);
217  return;
218  }
219 
220  // FIXME: Fetch these
221  client.SendAvatarPicksReply(targetAvatarID, new Dictionary<UUID, string>(0));
222  }
223 
224  private void HandlePickInfoRequest(Object sender, string method, List<String> args)
225  {
226  if (!(sender is IClientAPI))
227  return;
228  IClientAPI client = (IClientAPI)sender;
229 
230  UUID avatarID;
231  UUID pickID;
232  if (args.Count < 2 || !UUID.TryParse(args[0], out avatarID) || !UUID.TryParse(args[1], out pickID))
233  {
234  m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method);
235  return;
236  }
237 
238  // FIXME: Fetch this
239  client.SendPickInfoReply(pickID, avatarID, false, UUID.Zero, String.Empty, String.Empty, UUID.Zero, String.Empty,
240  String.Empty, String.Empty, Vector3.Zero, 0, false);
241  }
242 
243  private void PickInfoUpdateHandler(IClientAPI client, UUID pickID, UUID creatorID, bool topPick, string name,
244  string desc, UUID snapshotID, int sortOrder, bool enabled)
245  {
246  // FIXME: Save this
247  }
248 
249  private void PickDeleteHandler(IClientAPI client, UUID pickID)
250  {
251  // FIXME: Delete
252  }
253 
254  #endregion Picks
255 
256  #region Notes
257 
258  private void HandleAvatarNotesRequest(Object sender, string method, List<String> args)
259  {
260  if (!(sender is IClientAPI))
261  return;
262  IClientAPI client = (IClientAPI)sender;
263 
264  UUID targetAvatarID;
265  if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID))
266  {
267  m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method);
268  return;
269  }
270 
271  // FIXME: Fetch this
272  client.SendAvatarNotesReply(targetAvatarID, String.Empty);
273  }
274 
275  private void AvatarNotesUpdateHandler(IClientAPI client, UUID targetID, string notes)
276  {
277  // FIXME: Save this
278  }
279 
280  #endregion Notes
281 
282  #region Profiles
283 
284  private void RequestAvatarPropertiesHandler(IClientAPI client, UUID avatarID)
285  {
286  m_log.DebugFormat("[SIMIAN PROFILES]: Request avatar properties for {0}",avatarID);
287 
288  OSDMap user = FetchUserData(avatarID);
289 
290  ProfileFlags flags = ProfileFlags.AllowPublish | ProfileFlags.MaturePublish;
291 
292  if (user != null)
293  {
294  OSDMap about = null;
295  if (user.ContainsKey("LLAbout"))
296  {
297  try
298  {
299  about = OSDParser.DeserializeJson(user["LLAbout"].AsString()) as OSDMap;
300  }
301  catch
302  {
303  m_log.WarnFormat("[SIMIAN PROFILES]: Unable to decode LLAbout");
304  }
305  }
306 
307  if (about == null)
308  about = new OSDMap(0);
309 
310  // Check if this user is a grid operator
311  byte[] charterMember;
312  if (user["AccessLevel"].AsInteger() >= 200)
313  charterMember = Utils.StringToBytes("Operator");
314  else
315  charterMember = Utils.EmptyBytes;
316 
317  // Check if the user is online
318  if (client.Scene is Scene)
319  {
320  OpenSim.Services.Interfaces.PresenceInfo[] presences = ((Scene)client.Scene).PresenceService.GetAgents(new string[] { avatarID.ToString() });
321  if (presences != null && presences.Length > 0)
322  flags |= ProfileFlags.Online;
323  }
324 
325  // Check if the user is identified
326  if (user["Identified"].AsBoolean())
327  flags |= ProfileFlags.Identified;
328 
329  client.SendAvatarProperties(avatarID, about["About"].AsString(), user["CreationDate"].AsDate().ToString("M/d/yyyy",
330  System.Globalization.CultureInfo.InvariantCulture), charterMember, about["FLAbout"].AsString(), (uint)flags,
331  about["FLImage"].AsUUID(), about["Image"].AsUUID(), about["URL"].AsString(), user["Partner"].AsUUID());
332 
333  OSDMap interests = null;
334  if (user.ContainsKey("LLInterests"))
335  {
336  try
337  {
338  interests = OSDParser.DeserializeJson(user["LLInterests"].AsString()) as OSDMap;
339  client.SendAvatarInterestsReply(avatarID, interests["WantMask"].AsUInteger(), interests["WantText"].AsString(), interests["SkillsMask"].AsUInteger(), interests["SkillsText"].AsString(), interests["Languages"].AsString());
340  }
341  catch { }
342  }
343 
344  if (about == null)
345  about = new OSDMap(0);
346  }
347  else
348  {
349  m_log.Warn("[SIMIAN PROFILES]: Failed to fetch profile information for " + client.Name + ", returning default values");
350  client.SendAvatarProperties(avatarID, String.Empty, "1/1/1970", Utils.EmptyBytes,
351  String.Empty, (uint)flags, UUID.Zero, UUID.Zero, String.Empty, UUID.Zero);
352  }
353  }
354 
355  private void UpdateAvatarPropertiesHandler(IClientAPI client, UserProfileData profileData)
356  {
357  OSDMap map = new OSDMap
358  {
359  { "About", OSD.FromString(profileData.AboutText) },
360  { "Image", OSD.FromUUID(profileData.Image) },
361  { "FLAbout", OSD.FromString(profileData.FirstLifeAboutText) },
362  { "FLImage", OSD.FromUUID(profileData.FirstLifeImage) },
363  { "URL", OSD.FromString(profileData.ProfileUrl) }
364  };
365 
366  AddUserData(client.AgentId, "LLAbout", map);
367  }
368 
369  private void AvatarInterestUpdateHandler(IClientAPI client, uint wantmask, string wanttext, uint skillsmask,
370  string skillstext, string languages)
371  {
372  OSDMap map = new OSDMap
373  {
374  { "WantMask", OSD.FromInteger(wantmask) },
375  { "WantText", OSD.FromString(wanttext) },
376  { "SkillsMask", OSD.FromInteger(skillsmask) },
377  { "SkillsText", OSD.FromString(skillstext) },
378  { "Languages", OSD.FromString(languages) }
379  };
380 
381  AddUserData(client.AgentId, "LLInterests", map);
382  }
383 
384  private void UserInfoRequestHandler(IClientAPI client)
385  {
386  m_log.Error("[SIMIAN PROFILES]: UserInfoRequestHandler");
387 
388  // Fetch this user's e-mail address
389  NameValueCollection requestArgs = new NameValueCollection
390  {
391  { "RequestMethod", "GetUser" },
392  { "UserID", client.AgentId.ToString() }
393  };
394 
395  OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs);
396  string email = response["Email"].AsString();
397 
398  if (!response["Success"].AsBoolean())
399  m_log.Warn("[SIMIAN PROFILES]: GetUser failed during a user info request for " + client.Name);
400 
401  client.SendUserInfoReply(false, true, email);
402  }
403 
404  private void UpdateUserInfoHandler(bool imViaEmail, bool visible, IClientAPI client)
405  {
406  m_log.Info("[SIMIAN PROFILES]: Ignoring user info update from " + client.Name);
407  }
408 
409  #endregion Profiles
410 
414  private void CheckEstateManager(Scene scene)
415  {
416  EstateSettings estate = scene.RegionInfo.EstateSettings;
417 
418  if (estate.EstateOwner == UUID.Zero)
419  {
420  // Attempt to lookup the grid admin
421  UserAccount admin = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, UUID.Zero);
422  if (admin != null)
423  {
424  m_log.InfoFormat("[SIMIAN PROFILES]: Setting estate {0} (ID: {1}) owner to {2}", estate.EstateName,
425  estate.EstateID, admin.Name);
426 
427  estate.EstateOwner = admin.PrincipalID;
428  scene.EstateDataService.StoreEstateSettings(estate);
429  }
430  else
431  {
432  m_log.WarnFormat("[SIMIAN PROFILES]: Estate {0} (ID: {1}) does not have an owner", estate.EstateName, estate.EstateID);
433  }
434  }
435  }
436 
437  private bool AddUserData(UUID userID, string key, OSDMap value)
438  {
439  NameValueCollection requestArgs = new NameValueCollection
440  {
441  { "RequestMethod", "AddUserData" },
442  { "UserID", userID.ToString() },
443  { key, OSDParser.SerializeJsonString(value) }
444  };
445 
446  OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs);
447  bool success = response["Success"].AsBoolean();
448 
449  if (!success)
450  m_log.WarnFormat("[SIMIAN PROFILES]: Failed to add user data with key {0} for {1}: {2}", key, userID, response["Message"].AsString());
451 
452  return success;
453  }
454 
455  private OSDMap FetchUserData(UUID userID)
456  {
457  m_log.DebugFormat("[SIMIAN PROFILES]: Fetch information about {0}",userID);
458 
459  NameValueCollection requestArgs = new NameValueCollection
460  {
461  { "RequestMethod", "GetUser" },
462  { "UserID", userID.ToString() }
463  };
464 
465  OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs);
466  if (response["Success"].AsBoolean() && response["User"] is OSDMap)
467  {
468  return (OSDMap)response["User"];
469  }
470  else
471  {
472  m_log.Error("[SIMIAN PROFILES]: Failed to fetch user data for " + userID + ": " + response["Message"].AsString());
473  }
474 
475  return null;
476  }
477  }
478 }
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
string Name
Returns the full name of the agent/avatar represented by this client
Definition: IClientAPI.cs:754
OpenMetaverse.StructuredData.OSDMap OSDMap
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
Definition: ICM_Api.cs:31
Information about a particular user known to the userserver
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
Connects avatar profile and classified queries to the SimianGrid backend
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...