clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 {
45 /// <summary>
46 /// Avatar profile flags
47 /// </summary>
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  
58 /// <summary>
59 /// Connects avatar profile and classified queries to the SimianGrid
60 /// backend
61 /// </summary>
62 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianProfiles")]
63 public class SimianProfiles : INonSharedRegionModule
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  
411 /// <summary>
412 /// Sanity checks regions for a valid estate owner at startup
413 /// </summary>
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 }