opensim – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | eva | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ |
||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
||
4 | * |
||
5 | * Redistribution and use in source and binary forms, with or without |
||
6 | * modification, are permitted provided that the following conditions are met: |
||
7 | * * Redistributions of source code must retain the above copyright |
||
8 | * notice, this list of conditions and the following disclaimer. |
||
9 | * * Redistributions in binary form must reproduce the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer in the |
||
11 | * documentation and/or other materials provided with the distribution. |
||
12 | * * Neither the name of the OpenSimulator Project nor the |
||
13 | * names of its contributors may be used to endorse or promote products |
||
14 | * derived from this software without specific prior written permission. |
||
15 | * |
||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY |
||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY |
||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
26 | */ |
||
27 | using System; |
||
28 | using System.Collections.Generic; |
||
29 | using System.IO; |
||
30 | using System.Reflection; |
||
31 | using System.Threading; |
||
32 | |||
33 | using OpenSim.Framework; |
||
34 | using OpenSim.Framework.Console; |
||
35 | using OpenSim.Framework.Monitoring; |
||
36 | using OpenSim.Region.ClientStack.LindenUDP; |
||
37 | using OpenSim.Region.Framework; |
||
38 | using OpenSim.Region.Framework.Interfaces; |
||
39 | using OpenSim.Region.Framework.Scenes; |
||
40 | using OpenSim.Services.Interfaces; |
||
41 | using OpenSim.Services.Connectors.Hypergrid; |
||
42 | |||
43 | using OpenMetaverse; |
||
44 | using OpenMetaverse.Packets; |
||
45 | using log4net; |
||
46 | using Nini.Config; |
||
47 | using Mono.Addins; |
||
48 | |||
49 | using DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; |
||
50 | |||
51 | namespace OpenSim.Region.CoreModules.Framework.UserManagement |
||
52 | { |
||
53 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UserManagementModule")] |
||
54 | public class UserManagementModule : ISharedRegionModule, IUserManagement, IPeople |
||
55 | { |
||
56 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
||
57 | |||
58 | protected bool m_Enabled; |
||
59 | protected List<Scene> m_Scenes = new List<Scene>(); |
||
60 | |||
61 | protected IServiceThrottleModule m_ServiceThrottle; |
||
62 | // The cache |
||
63 | protected Dictionary<UUID, UserData> m_UserCache = new Dictionary<UUID, UserData>(); |
||
64 | |||
65 | #region ISharedRegionModule |
||
66 | |||
67 | public void Initialise(IConfigSource config) |
||
68 | { |
||
69 | string umanmod = config.Configs["Modules"].GetString("UserManagementModule", Name); |
||
70 | if (umanmod == Name) |
||
71 | { |
||
72 | m_Enabled = true; |
||
73 | Init(); |
||
74 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: {0} is enabled", Name); |
||
75 | } |
||
76 | } |
||
77 | |||
78 | public bool IsSharedModule |
||
79 | { |
||
80 | get { return true; } |
||
81 | } |
||
82 | |||
83 | public virtual string Name |
||
84 | { |
||
85 | get { return "BasicUserManagementModule"; } |
||
86 | } |
||
87 | |||
88 | public Type ReplaceableInterface |
||
89 | { |
||
90 | get { return null; } |
||
91 | } |
||
92 | |||
93 | public void AddRegion(Scene scene) |
||
94 | { |
||
95 | if (m_Enabled) |
||
96 | { |
||
97 | m_Scenes.Add(scene); |
||
98 | |||
99 | scene.RegisterModuleInterface<IUserManagement>(this); |
||
100 | scene.RegisterModuleInterface<IPeople>(this); |
||
101 | scene.EventManager.OnNewClient += new EventManager.OnNewClientDelegate(EventManager_OnNewClient); |
||
102 | scene.EventManager.OnPrimsLoaded += new EventManager.PrimsLoaded(EventManager_OnPrimsLoaded); |
||
103 | } |
||
104 | } |
||
105 | |||
106 | public void RemoveRegion(Scene scene) |
||
107 | { |
||
108 | if (m_Enabled) |
||
109 | { |
||
110 | scene.UnregisterModuleInterface<IUserManagement>(this); |
||
111 | m_Scenes.Remove(scene); |
||
112 | } |
||
113 | } |
||
114 | |||
115 | public void RegionLoaded(Scene s) |
||
116 | { |
||
117 | if (m_Enabled && m_ServiceThrottle == null) |
||
118 | m_ServiceThrottle = s.RequestModuleInterface<IServiceThrottleModule>(); |
||
119 | } |
||
120 | |||
121 | public void PostInitialise() |
||
122 | { |
||
123 | } |
||
124 | |||
125 | public void Close() |
||
126 | { |
||
127 | m_Scenes.Clear(); |
||
128 | |||
129 | lock (m_UserCache) |
||
130 | m_UserCache.Clear(); |
||
131 | } |
||
132 | |||
133 | #endregion ISharedRegionModule |
||
134 | |||
135 | |||
136 | #region Event Handlers |
||
137 | |||
138 | void EventManager_OnPrimsLoaded(Scene s) |
||
139 | { |
||
140 | // let's sniff all the user names referenced by objects in the scene |
||
141 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: Caching creators' data from {0} ({1} objects)...", s.RegionInfo.RegionName, s.GetEntities().Length); |
||
142 | s.ForEachSOG(delegate(SceneObjectGroup sog) { CacheCreators(sog); }); |
||
143 | } |
||
144 | |||
145 | void EventManager_OnNewClient(IClientAPI client) |
||
146 | { |
||
147 | client.OnConnectionClosed += new Action<IClientAPI>(HandleConnectionClosed); |
||
148 | client.OnNameFromUUIDRequest += new UUIDNameRequest(HandleUUIDNameRequest); |
||
149 | client.OnAvatarPickerRequest += new AvatarPickerRequest(HandleAvatarPickerRequest); |
||
150 | } |
||
151 | |||
152 | void HandleConnectionClosed(IClientAPI client) |
||
153 | { |
||
154 | client.OnNameFromUUIDRequest -= new UUIDNameRequest(HandleUUIDNameRequest); |
||
155 | client.OnAvatarPickerRequest -= new AvatarPickerRequest(HandleAvatarPickerRequest); |
||
156 | } |
||
157 | |||
158 | void HandleUUIDNameRequest(UUID uuid, IClientAPI client) |
||
159 | { |
||
160 | // m_log.DebugFormat( |
||
161 | // "[USER MANAGEMENT MODULE]: Handling request for name binding of UUID {0} from {1}", |
||
162 | // uuid, remote_client.Name); |
||
163 | |||
164 | if (m_Scenes[0].LibraryService != null && (m_Scenes[0].LibraryService.LibraryRootFolder.Owner == uuid)) |
||
165 | { |
||
166 | client.SendNameReply(uuid, "Mr", "OpenSim"); |
||
167 | } |
||
168 | else |
||
169 | { |
||
170 | string[] names = new string[2]; |
||
171 | if (TryGetUserNamesFromCache(uuid, names)) |
||
172 | { |
||
173 | client.SendNameReply(uuid, names[0], names[1]); |
||
174 | return; |
||
175 | } |
||
176 | |||
177 | // Not found in cache, queue continuation |
||
178 | m_ServiceThrottle.Enqueue("name", uuid.ToString(), delegate |
||
179 | { |
||
180 | //m_log.DebugFormat("[YYY]: Name request {0}", uuid); |
||
181 | |||
182 | // As least upto September 2013, clients permanently cache UUID -> Name bindings. Some clients |
||
183 | // appear to clear this when the user asks it to clear the cache, but others may not. |
||
184 | // |
||
185 | // So to avoid clients |
||
186 | // (particularly Hypergrid clients) permanently binding "Unknown User" to a given UUID, we will |
||
187 | // instead drop the request entirely. |
||
188 | if (TryGetUserNames(uuid, names)) |
||
189 | client.SendNameReply(uuid, names[0], names[1]); |
||
190 | // else |
||
191 | // m_log.DebugFormat( |
||
192 | // "[USER MANAGEMENT MODULE]: No bound name for {0} found, ignoring request from {1}", |
||
193 | // uuid, client.Name); |
||
194 | }); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | public void HandleAvatarPickerRequest(IClientAPI client, UUID avatarID, UUID RequestID, string query) |
||
199 | { |
||
200 | //EventManager.TriggerAvatarPickerRequest(); |
||
201 | |||
202 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: HandleAvatarPickerRequest for {0}", query); |
||
203 | |||
204 | List<UserData> users = GetUserData(query, 500, 1); |
||
205 | |||
206 | AvatarPickerReplyPacket replyPacket = (AvatarPickerReplyPacket)PacketPool.Instance.GetPacket(PacketType.AvatarPickerReply); |
||
207 | // TODO: don't create new blocks if recycling an old packet |
||
208 | |||
209 | AvatarPickerReplyPacket.DataBlock[] searchData = |
||
210 | new AvatarPickerReplyPacket.DataBlock[users.Count]; |
||
211 | AvatarPickerReplyPacket.AgentDataBlock agentData = new AvatarPickerReplyPacket.AgentDataBlock(); |
||
212 | |||
213 | agentData.AgentID = avatarID; |
||
214 | agentData.QueryID = RequestID; |
||
215 | replyPacket.AgentData = agentData; |
||
216 | //byte[] bytes = new byte[AvatarResponses.Count*32]; |
||
217 | |||
218 | int i = 0; |
||
219 | foreach (UserData item in users) |
||
220 | { |
||
221 | UUID translatedIDtem = item.Id; |
||
222 | searchData[i] = new AvatarPickerReplyPacket.DataBlock(); |
||
223 | searchData[i].AvatarID = translatedIDtem; |
||
224 | searchData[i].FirstName = Utils.StringToBytes((string)item.FirstName); |
||
225 | searchData[i].LastName = Utils.StringToBytes((string)item.LastName); |
||
226 | i++; |
||
227 | } |
||
228 | if (users.Count == 0) |
||
229 | { |
||
230 | searchData = new AvatarPickerReplyPacket.DataBlock[0]; |
||
231 | } |
||
232 | replyPacket.Data = searchData; |
||
233 | |||
234 | AvatarPickerReplyAgentDataArgs agent_data = new AvatarPickerReplyAgentDataArgs(); |
||
235 | agent_data.AgentID = replyPacket.AgentData.AgentID; |
||
236 | agent_data.QueryID = replyPacket.AgentData.QueryID; |
||
237 | |||
238 | List<AvatarPickerReplyDataArgs> data_args = new List<AvatarPickerReplyDataArgs>(); |
||
239 | for (i = 0; i < replyPacket.Data.Length; i++) |
||
240 | { |
||
241 | AvatarPickerReplyDataArgs data_arg = new AvatarPickerReplyDataArgs(); |
||
242 | data_arg.AvatarID = replyPacket.Data[i].AvatarID; |
||
243 | data_arg.FirstName = replyPacket.Data[i].FirstName; |
||
244 | data_arg.LastName = replyPacket.Data[i].LastName; |
||
245 | data_args.Add(data_arg); |
||
246 | } |
||
247 | client.SendAvatarPickerReply(agent_data, data_args); |
||
248 | } |
||
249 | |||
250 | protected virtual void AddAdditionalUsers(string query, List<UserData> users) |
||
251 | { |
||
252 | } |
||
253 | |||
254 | #endregion Event Handlers |
||
255 | |||
256 | #region IPeople |
||
257 | |||
258 | public List<UserData> GetUserData(string query, int page_size, int page_number) |
||
259 | { |
||
260 | // search the user accounts service |
||
261 | List<UserAccount> accs = m_Scenes[0].UserAccountService.GetUserAccounts(m_Scenes[0].RegionInfo.ScopeID, query); |
||
262 | |||
263 | List<UserData> users = new List<UserData>(); |
||
264 | if (accs != null) |
||
265 | { |
||
266 | foreach (UserAccount acc in accs) |
||
267 | { |
||
268 | UserData ud = new UserData(); |
||
269 | ud.FirstName = acc.FirstName; |
||
270 | ud.LastName = acc.LastName; |
||
271 | ud.Id = acc.PrincipalID; |
||
272 | users.Add(ud); |
||
273 | } |
||
274 | } |
||
275 | |||
276 | // search the local cache |
||
277 | lock (m_UserCache) |
||
278 | { |
||
279 | foreach (UserData data in m_UserCache.Values) |
||
280 | { |
||
281 | if (users.Find(delegate(UserData d) { return d.Id == data.Id; }) == null && |
||
282 | (data.FirstName.ToLower().StartsWith(query.ToLower()) || data.LastName.ToLower().StartsWith(query.ToLower()))) |
||
283 | users.Add(data); |
||
284 | } |
||
285 | } |
||
286 | |||
287 | AddAdditionalUsers(query, users); |
||
288 | |||
289 | return users; |
||
290 | |||
291 | } |
||
292 | |||
293 | #endregion IPeople |
||
294 | |||
295 | private void CacheCreators(SceneObjectGroup sog) |
||
296 | { |
||
297 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: processing {0} {1}; {2}", sog.RootPart.Name, sog.RootPart.CreatorData, sog.RootPart.CreatorIdentification); |
||
298 | AddUser(sog.RootPart.CreatorID, sog.RootPart.CreatorData); |
||
299 | |||
300 | foreach (SceneObjectPart sop in sog.Parts) |
||
301 | { |
||
302 | AddUser(sop.CreatorID, sop.CreatorData); |
||
303 | foreach (TaskInventoryItem item in sop.TaskInventory.Values) |
||
304 | AddUser(item.CreatorID, item.CreatorData); |
||
305 | } |
||
306 | } |
||
307 | |||
308 | /// <summary> |
||
309 | /// |
||
310 | /// </summary> |
||
311 | /// <param name="uuid"></param> |
||
312 | /// <param name="names">Caller please provide a properly instantiated array for names, string[2]</param> |
||
313 | /// <returns></returns> |
||
314 | private bool TryGetUserNames(UUID uuid, string[] names) |
||
315 | { |
||
316 | if (names == null) |
||
317 | names = new string[2]; |
||
318 | |||
319 | if (TryGetUserNamesFromCache(uuid, names)) |
||
320 | return true; |
||
321 | |||
322 | if (TryGetUserNamesFromServices(uuid, names)) |
||
323 | return true; |
||
324 | |||
325 | return false; |
||
326 | } |
||
327 | |||
328 | private bool TryGetUserNamesFromCache(UUID uuid, string[] names) |
||
329 | { |
||
330 | lock (m_UserCache) |
||
331 | { |
||
332 | if (m_UserCache.ContainsKey(uuid)) |
||
333 | { |
||
334 | names[0] = m_UserCache[uuid].FirstName; |
||
335 | names[1] = m_UserCache[uuid].LastName; |
||
336 | |||
337 | return true; |
||
338 | } |
||
339 | } |
||
340 | |||
341 | return false; |
||
342 | } |
||
343 | |||
344 | /// <summary> |
||
345 | /// Try to get the names bound to the given uuid, from the services. |
||
346 | /// </summary> |
||
347 | /// <returns>True if the name was found, false if not.</returns> |
||
348 | /// <param name='uuid'></param> |
||
349 | /// <param name='names'>The array of names if found. If not found, then names[0] = "Unknown" and names[1] = "User"</param> |
||
350 | private bool TryGetUserNamesFromServices(UUID uuid, string[] names) |
||
351 | { |
||
352 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(UUID.Zero, uuid); |
||
353 | |||
354 | if (account != null) |
||
355 | { |
||
356 | names[0] = account.FirstName; |
||
357 | names[1] = account.LastName; |
||
358 | |||
359 | UserData user = new UserData(); |
||
360 | user.FirstName = account.FirstName; |
||
361 | user.LastName = account.LastName; |
||
362 | |||
363 | lock (m_UserCache) |
||
364 | m_UserCache[uuid] = user; |
||
365 | |||
366 | return true; |
||
367 | } |
||
368 | else |
||
369 | { |
||
370 | // Let's try the GridUser service |
||
371 | GridUserInfo uInfo = m_Scenes[0].GridUserService.GetGridUserInfo(uuid.ToString()); |
||
372 | if (uInfo != null) |
||
373 | { |
||
374 | string url, first, last, tmp; |
||
375 | UUID u; |
||
376 | if (Util.ParseUniversalUserIdentifier(uInfo.UserID, out u, out url, out first, out last, out tmp)) |
||
377 | { |
||
378 | AddUser(uuid, first, last, url); |
||
379 | |||
380 | if (m_UserCache.ContainsKey(uuid)) |
||
381 | { |
||
382 | names[0] = m_UserCache[uuid].FirstName; |
||
383 | names[1] = m_UserCache[uuid].LastName; |
||
384 | |||
385 | return true; |
||
386 | } |
||
387 | } |
||
388 | else |
||
389 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: Unable to parse UUI {0}", uInfo.UserID); |
||
390 | } |
||
391 | else |
||
392 | { |
||
393 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: No grid user found for {0}", uuid); |
||
394 | } |
||
395 | |||
396 | names[0] = "Unknown"; |
||
397 | names[1] = "UserUMMTGUN9"; |
||
398 | |||
399 | return false; |
||
400 | } |
||
401 | } |
||
402 | |||
403 | #region IUserManagement |
||
404 | |||
405 | public UUID GetUserIdByName(string name) |
||
406 | { |
||
407 | string[] parts = name.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); |
||
408 | if (parts.Length < 2) |
||
409 | throw new Exception("Name must have 2 components"); |
||
410 | |||
411 | return GetUserIdByName(parts[0], parts[1]); |
||
412 | } |
||
413 | |||
414 | public UUID GetUserIdByName(string firstName, string lastName) |
||
415 | { |
||
416 | // TODO: Optimize for reverse lookup if this gets used by non-console commands. |
||
417 | lock (m_UserCache) |
||
418 | { |
||
419 | foreach (UserData user in m_UserCache.Values) |
||
420 | { |
||
421 | if (user.FirstName == firstName && user.LastName == lastName) |
||
422 | return user.Id; |
||
423 | } |
||
424 | } |
||
425 | |||
426 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(UUID.Zero, firstName, lastName); |
||
427 | |||
428 | if (account != null) |
||
429 | return account.PrincipalID; |
||
430 | |||
431 | return UUID.Zero; |
||
432 | } |
||
433 | |||
434 | public string GetUserName(UUID uuid) |
||
435 | { |
||
436 | string[] names = new string[2]; |
||
437 | TryGetUserNames(uuid, names); |
||
438 | |||
439 | return names[0] + " " + names[1]; |
||
440 | |||
441 | } |
||
442 | |||
443 | public string GetUserHomeURL(UUID userID) |
||
444 | { |
||
445 | lock (m_UserCache) |
||
446 | { |
||
447 | if (m_UserCache.ContainsKey(userID)) |
||
448 | return m_UserCache[userID].HomeURL; |
||
449 | } |
||
450 | |||
451 | return string.Empty; |
||
452 | } |
||
453 | |||
454 | public string GetUserServerURL(UUID userID, string serverType) |
||
455 | { |
||
456 | UserData userdata; |
||
457 | lock (m_UserCache) |
||
458 | m_UserCache.TryGetValue(userID, out userdata); |
||
459 | |||
460 | if (userdata != null) |
||
461 | { |
||
462 | // m_log.DebugFormat("[USER MANAGEMENT MODULE]: Requested url type {0} for {1}", serverType, userID); |
||
463 | |||
464 | if (userdata.ServerURLs != null && userdata.ServerURLs.ContainsKey(serverType) && userdata.ServerURLs[serverType] != null) |
||
465 | { |
||
466 | return userdata.ServerURLs[serverType].ToString(); |
||
467 | } |
||
468 | |||
469 | if (!string.IsNullOrEmpty(userdata.HomeURL)) |
||
470 | { |
||
471 | //m_log.DebugFormat( |
||
472 | // "[USER MANAGEMENT MODULE]: Did not find url type {0} so requesting urls from '{1}' for {2}", |
||
473 | // serverType, userdata.HomeURL, userID); |
||
474 | |||
475 | UserAgentServiceConnector uConn = new UserAgentServiceConnector(userdata.HomeURL); |
||
476 | userdata.ServerURLs = uConn.GetServerURLs(userID); |
||
477 | if (userdata.ServerURLs != null && userdata.ServerURLs.ContainsKey(serverType) && userdata.ServerURLs[serverType] != null) |
||
478 | return userdata.ServerURLs[serverType].ToString(); |
||
479 | } |
||
480 | } |
||
481 | |||
482 | return string.Empty; |
||
483 | } |
||
484 | |||
485 | public string GetUserUUI(UUID userID) |
||
486 | { |
||
487 | UserData ud; |
||
488 | lock (m_UserCache) |
||
489 | m_UserCache.TryGetValue(userID, out ud); |
||
490 | |||
491 | if (ud == null) // It's not in the cache |
||
492 | { |
||
493 | string[] names = new string[2]; |
||
494 | // This will pull the data from either UserAccounts or GridUser |
||
495 | // and stick it into the cache |
||
496 | TryGetUserNamesFromServices(userID, names); |
||
497 | lock (m_UserCache) |
||
498 | m_UserCache.TryGetValue(userID, out ud); |
||
499 | } |
||
500 | |||
501 | if (ud != null) |
||
502 | { |
||
503 | string homeURL = ud.HomeURL; |
||
504 | string first = ud.FirstName, last = ud.LastName; |
||
505 | if (ud.LastName.StartsWith("@")) |
||
506 | { |
||
507 | string[] parts = ud.FirstName.Split('.'); |
||
508 | if (parts.Length >= 2) |
||
509 | { |
||
510 | first = parts[0]; |
||
511 | last = parts[1]; |
||
512 | } |
||
513 | return userID + ";" + homeURL + ";" + first + " " + last; |
||
514 | } |
||
515 | } |
||
516 | |||
517 | return userID.ToString(); |
||
518 | } |
||
519 | |||
520 | public void AddUser(UUID uuid, string first, string last) |
||
521 | { |
||
522 | lock (m_UserCache) |
||
523 | { |
||
524 | if (m_UserCache.ContainsKey(uuid)) |
||
525 | return; |
||
526 | } |
||
527 | |||
528 | UserData user = new UserData(); |
||
529 | user.Id = uuid; |
||
530 | user.FirstName = first; |
||
531 | user.LastName = last; |
||
532 | |||
533 | AddUserInternal(user); |
||
534 | } |
||
535 | |||
536 | public void AddUser(UUID uuid, string first, string last, string homeURL) |
||
537 | { |
||
538 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL); |
||
539 | if (homeURL == string.Empty) |
||
540 | return; |
||
541 | |||
542 | AddUser(uuid, homeURL + ";" + first + " " + last); |
||
543 | } |
||
544 | |||
545 | public void AddUser(UUID id, string creatorData) |
||
546 | { |
||
547 | //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, creatorData {1}", id, creatorData); |
||
548 | |||
549 | UserData oldUser; |
||
550 | lock (m_UserCache) |
||
551 | m_UserCache.TryGetValue(id, out oldUser); |
||
552 | |||
553 | if (oldUser != null) |
||
554 | { |
||
555 | if (string.IsNullOrEmpty(creatorData)) |
||
556 | { |
||
557 | //ignore updates without creator data |
||
558 | return; |
||
559 | } |
||
560 | |||
561 | //try update unknown users, but don't update anyone else |
||
562 | if (oldUser.FirstName == "Unknown" && !creatorData.Contains("Unknown")) |
||
563 | { |
||
564 | lock (m_UserCache) |
||
565 | m_UserCache.Remove(id); |
||
566 | m_log.DebugFormat("[USER MANAGEMENT MODULE]: Re-adding user with id {0}, creatorData [{1}] and old HomeURL {2}", id, creatorData, oldUser.HomeURL); |
||
567 | } |
||
568 | else |
||
569 | { |
||
570 | //we have already a valid user within the cache |
||
571 | return; |
||
572 | } |
||
573 | } |
||
574 | |||
575 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, id); |
||
576 | |||
577 | if (account != null) |
||
578 | { |
||
579 | AddUser(id, account.FirstName, account.LastName); |
||
580 | } |
||
581 | else |
||
582 | { |
||
583 | UserData user = new UserData(); |
||
584 | user.Id = id; |
||
585 | |||
586 | if (!string.IsNullOrEmpty(creatorData)) |
||
587 | { |
||
588 | //creatorData = <endpoint>;<name> |
||
589 | |||
590 | string[] parts = creatorData.Split(';'); |
||
591 | if (parts.Length >= 1) |
||
592 | { |
||
593 | user.HomeURL = parts[0]; |
||
594 | try |
||
595 | { |
||
596 | Uri uri = new Uri(parts[0]); |
||
597 | user.LastName = "@" + uri.Authority; |
||
598 | } |
||
599 | catch (UriFormatException) |
||
600 | { |
||
601 | m_log.DebugFormat("[SCENE]: Unable to parse Uri {0}", parts[0]); |
||
602 | user.LastName = "@unknown"; |
||
603 | } |
||
604 | } |
||
605 | |||
606 | if (parts.Length >= 2) |
||
607 | user.FirstName = parts[1].Replace(' ', '.'); |
||
608 | } |
||
609 | else |
||
610 | { |
||
611 | // Temporarily add unknown user entries of this type into the cache so that we can distinguish |
||
612 | // this source from other recent (hopefully resolved) bugs that fail to retrieve a user name binding |
||
613 | // TODO: Can be removed when GUN* unknown users have definitely dropped significantly or |
||
614 | // disappeared. |
||
615 | user.FirstName = "Unknown"; |
||
616 | user.LastName = "UserUMMAU4"; |
||
617 | } |
||
618 | |||
619 | AddUserInternal(user); |
||
620 | } |
||
621 | } |
||
622 | |||
623 | void AddUserInternal(UserData user) |
||
624 | { |
||
625 | lock (m_UserCache) |
||
626 | m_UserCache[user.Id] = user; |
||
627 | |||
628 | //m_log.DebugFormat( |
||
629 | // "[USER MANAGEMENT MODULE]: Added user {0} {1} {2} {3}", |
||
630 | // user.Id, user.FirstName, user.LastName, user.HomeURL); |
||
631 | } |
||
632 | |||
633 | public bool IsLocalGridUser(UUID uuid) |
||
634 | { |
||
635 | UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, uuid); |
||
636 | if (account == null || (account != null && !account.LocalToGrid)) |
||
637 | return false; |
||
638 | |||
639 | return true; |
||
640 | } |
||
641 | |||
642 | #endregion IUserManagement |
||
643 | |||
644 | protected void Init() |
||
645 | { |
||
646 | AddUser(UUID.Zero, "Unknown", "User"); |
||
647 | RegisterConsoleCmds(); |
||
648 | } |
||
649 | |||
650 | protected void RegisterConsoleCmds() |
||
651 | { |
||
652 | MainConsole.Instance.Commands.AddCommand("Users", true, |
||
653 | "show name", |
||
654 | "show name <uuid>", |
||
655 | "Show the bindings between a single user UUID and a user name", |
||
656 | String.Empty, |
||
657 | HandleShowUser); |
||
658 | |||
659 | MainConsole.Instance.Commands.AddCommand("Users", true, |
||
660 | "show names", |
||
661 | "show names", |
||
662 | "Show the bindings between user UUIDs and user names", |
||
663 | String.Empty, |
||
664 | HandleShowUsers); |
||
665 | } |
||
666 | |||
667 | private void HandleShowUser(string module, string[] cmd) |
||
668 | { |
||
669 | if (cmd.Length < 3) |
||
670 | { |
||
671 | MainConsole.Instance.OutputFormat("Usage: show name <uuid>"); |
||
672 | return; |
||
673 | } |
||
674 | |||
675 | UUID userId; |
||
676 | if (!ConsoleUtil.TryParseConsoleUuid(MainConsole.Instance, cmd[2], out userId)) |
||
677 | return; |
||
678 | |||
679 | string[] names; |
||
680 | |||
681 | UserData ud; |
||
682 | |||
683 | lock (m_UserCache) |
||
684 | { |
||
685 | if (!m_UserCache.TryGetValue(userId, out ud)) |
||
686 | { |
||
687 | MainConsole.Instance.OutputFormat("No name known for user with id {0}", userId); |
||
688 | return; |
||
689 | } |
||
690 | } |
||
691 | |||
692 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); |
||
693 | cdt.AddColumn("UUID", 36); |
||
694 | cdt.AddColumn("Name", 30); |
||
695 | cdt.AddColumn("HomeURL", 40); |
||
696 | cdt.AddRow(userId, string.Format("{0} {1}", ud.FirstName, ud.LastName), ud.HomeURL); |
||
697 | |||
698 | MainConsole.Instance.Output(cdt.ToString()); |
||
699 | } |
||
700 | |||
701 | private void HandleShowUsers(string module, string[] cmd) |
||
702 | { |
||
703 | ConsoleDisplayTable cdt = new ConsoleDisplayTable(); |
||
704 | cdt.AddColumn("UUID", 36); |
||
705 | cdt.AddColumn("Name", 30); |
||
706 | cdt.AddColumn("HomeURL", 40); |
||
707 | |||
708 | lock (m_UserCache) |
||
709 | { |
||
710 | foreach (KeyValuePair<UUID, UserData> kvp in m_UserCache) |
||
711 | cdt.AddRow(kvp.Key, string.Format("{0} {1}", kvp.Value.FirstName, kvp.Value.LastName), kvp.Value.HomeURL); |
||
712 | } |
||
713 | |||
714 | MainConsole.Instance.Output(cdt.ToString()); |
||
715 | } |
||
716 | |||
717 | } |
||
718 | |||
719 | } |