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.IO;
31 using System.Reflection;
32 using log4net;
33 using NDesk.Options;
34 using Nini.Config;
35 using OpenMetaverse;
36 using OpenSim.Framework;
37 using OpenSim.Framework.Communications;
38 using OpenSim.Framework.Console;
39 using OpenSim.Region.Framework.Interfaces;
40 using OpenSim.Region.Framework.Scenes;
41 using OpenSim.Services.Interfaces;
42 using Mono.Addins;
43  
44 namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
45 {
46 /// <summary>
47 /// This module loads and saves OpenSimulator inventory archives
48 /// </summary>
49 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "InventoryArchiverModule")]
50 public class InventoryArchiverModule : ISharedRegionModule, IInventoryArchiverModule
51 {
52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
53  
54 /// <value>
55 /// Enable or disable checking whether the iar user is actually logged in
56 /// </value>
57 // public bool DisablePresenceChecks { get; set; }
58  
59 public event InventoryArchiveSaved OnInventoryArchiveSaved;
60 public event InventoryArchiveLoaded OnInventoryArchiveLoaded;
61  
62 /// <summary>
63 /// The file to load and save inventory if no filename has been specified
64 /// </summary>
65 protected const string DEFAULT_INV_BACKUP_FILENAME = "user-inventory.iar";
66  
67 /// <value>
68 /// Pending save and load completions initiated from the console
69 /// </value>
70 protected List<UUID> m_pendingConsoleTasks = new List<UUID>();
71  
72 /// <value>
73 /// All scenes that this module knows about
74 /// </value>
75 private Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>();
76 private Scene m_aScene;
77  
78 private IUserAccountService m_UserAccountService;
79 protected IUserAccountService UserAccountService
80 {
81 get
82 {
83 if (m_UserAccountService == null)
84 // What a strange thing to do...
85 foreach (Scene s in m_scenes.Values)
86 {
87 m_UserAccountService = s.RequestModuleInterface<IUserAccountService>();
88 break;
89 }
90  
91 return m_UserAccountService;
92 }
93 }
94  
95  
96 public InventoryArchiverModule() {}
97  
98 // public InventoryArchiverModule(bool disablePresenceChecks)
99 // {
100 // DisablePresenceChecks = disablePresenceChecks;
101 // }
102  
103 #region ISharedRegionModule
104  
105 public void Initialise(IConfigSource source)
106 {
107 }
108  
109 public void AddRegion(Scene scene)
110 {
111 if (m_scenes.Count == 0)
112 {
113 scene.RegisterModuleInterface<IInventoryArchiverModule>(this);
114 OnInventoryArchiveSaved += SaveInvConsoleCommandCompleted;
115 OnInventoryArchiveLoaded += LoadInvConsoleCommandCompleted;
116  
117 scene.AddCommand(
118 "Archiving", this, "load iar",
119 "load iar [-m|--merge] <first> <last> <inventory path> [<IAR path>]",
120 "Load user inventory archive (IAR).",
121 "-m|--merge is an option which merges the loaded IAR with existing inventory folders where possible, rather than always creating new ones"
122 + "<first> is user's first name." + Environment.NewLine
123 + "<last> is user's last name." + Environment.NewLine
124 + "<inventory path> is the path inside the user's inventory where the IAR should be loaded." + Environment.NewLine
125 + "<IAR path> is the filesystem path or URI from which to load the IAR."
126 + string.Format(" If this is not given then the filename {0} in the current directory is used", DEFAULT_INV_BACKUP_FILENAME),
127 HandleLoadInvConsoleCommand);
128  
129 scene.AddCommand(
130 "Archiving", this, "save iar",
131 "save iar [-h|--home=<url>] [--noassets] <first> <last> <inventory path> [<IAR path>] [-c|--creators] [-e|--exclude=<name/uuid>] [-f|--excludefolder=<foldername/uuid>] [-v|--verbose]",
132 "Save user inventory archive (IAR).",
133 "<first> is the user's first name.\n"
134 + "<last> is the user's last name.\n"
135 + "<inventory path> is the path inside the user's inventory for the folder/item to be saved.\n"
136 + "<IAR path> is the filesystem path at which to save the IAR."
137 + string.Format(" If this is not given then the filename {0} in the current directory is used.\n", DEFAULT_INV_BACKUP_FILENAME)
138 + "-h|--home=<url> adds the url of the profile service to the saved user information.\n"
139 + "-c|--creators preserves information about foreign creators.\n"
140 + "-e|--exclude=<name/uuid> don't save the inventory item in archive" + Environment.NewLine
141 + "-f|--excludefolder=<folder/uuid> don't save contents of the folder in archive" + Environment.NewLine
142 + "-v|--verbose extra debug messages.\n"
143 + "--noassets stops assets being saved to the IAR."
144 + "--perm=<permissions> stops items with insufficient permissions from being saved to the IAR.\n"
145 + " <permissions> can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer, \"M\" = Modify.\n",
146 HandleSaveInvConsoleCommand);
147  
148 m_aScene = scene;
149 }
150  
151 m_scenes[scene.RegionInfo.RegionID] = scene;
152 }
153  
154 public void RemoveRegion(Scene scene)
155 {
156 }
157  
158 public void Close() {}
159  
160 public void RegionLoaded(Scene scene)
161 {
162 }
163  
164 public void PostInitialise()
165 {
166 }
167  
168 public Type ReplaceableInterface
169 {
170 get { return null; }
171 }
172  
173 public string Name { get { return "Inventory Archiver Module"; } }
174  
175 #endregion
176  
177 /// <summary>
178 /// Trigger the inventory archive saved event.
179 /// </summary>
180 protected internal void TriggerInventoryArchiveSaved(
181 UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream,
182 Exception reportedException, int SaveCount, int FilterCount)
183 {
184 InventoryArchiveSaved handlerInventoryArchiveSaved = OnInventoryArchiveSaved;
185 if (handlerInventoryArchiveSaved != null)
186 handlerInventoryArchiveSaved(id, succeeded, userInfo, invPath, saveStream, reportedException, SaveCount , FilterCount);
187 }
188  
189 /// <summary>
190 /// Trigger the inventory archive loaded event.
191 /// </summary>
192 protected internal void TriggerInventoryArchiveLoaded(
193 UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream loadStream,
194 Exception reportedException, int LoadCount)
195 {
196 InventoryArchiveLoaded handlerInventoryArchiveLoaded = OnInventoryArchiveLoaded;
197 if (handlerInventoryArchiveLoaded != null)
198 handlerInventoryArchiveLoaded(id, succeeded, userInfo, invPath, loadStream, reportedException, LoadCount);
199 }
200  
201 public bool ArchiveInventory(
202 UUID id, string firstName, string lastName, string invPath, Stream saveStream)
203 {
204 return ArchiveInventory(id, firstName, lastName, invPath, saveStream, new Dictionary<string, object>());
205 }
206  
207 public bool ArchiveInventory(
208 UUID id, string firstName, string lastName, string invPath, Stream saveStream,
209 Dictionary<string, object> options)
210 {
211 if (m_scenes.Count > 0)
212 {
213 UserAccount userInfo = GetUserInfo(firstName, lastName);
214  
215 if (userInfo != null)
216 {
217 // if (CheckPresence(userInfo.PrincipalID))
218 // {
219 try
220 {
221 new InventoryArchiveWriteRequest(id, this, m_aScene, userInfo, invPath, saveStream).Execute(options, UserAccountService);
222 }
223 catch (EntryPointNotFoundException e)
224 {
225 m_log.ErrorFormat(
226 "[INVENTORY ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
227 + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
228 m_log.Error(e);
229  
230 return false;
231 }
232  
233 return true;
234 // }
235 // else
236 // {
237 // m_log.ErrorFormat(
238 // "[INVENTORY ARCHIVER]: User {0} {1} {2} not logged in to this region simulator",
239 // userInfo.FirstName, userInfo.LastName, userInfo.PrincipalID);
240 // }
241 }
242 }
243  
244 return false;
245 }
246  
247 public bool ArchiveInventory(
248 UUID id, string firstName, string lastName, string invPath, string savePath,
249 Dictionary<string, object> options)
250 {
251 // if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, savePath))
252 // return false;
253  
254 if (m_scenes.Count > 0)
255 {
256 UserAccount userInfo = GetUserInfo(firstName, lastName);
257  
258 if (userInfo != null)
259 {
260 // if (CheckPresence(userInfo.PrincipalID))
261 // {
262 try
263 {
264 new InventoryArchiveWriteRequest(id, this, m_aScene, userInfo, invPath, savePath).Execute(options, UserAccountService);
265 }
266 catch (EntryPointNotFoundException e)
267 {
268 m_log.ErrorFormat(
269 "[INVENTORY ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
270 + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
271 m_log.Error(e);
272  
273 return false;
274 }
275  
276 return true;
277 // }
278 // else
279 // {
280 // m_log.ErrorFormat(
281 // "[INVENTORY ARCHIVER]: User {0} {1} {2} not logged in to this region simulator",
282 // userInfo.FirstName, userInfo.LastName, userInfo.PrincipalID);
283 // }
284 }
285 }
286  
287 return false;
288 }
289  
290 public bool DearchiveInventory(UUID id, string firstName, string lastName, string invPath, Stream loadStream)
291 {
292 return DearchiveInventory(id, firstName, lastName, invPath, loadStream, new Dictionary<string, object>());
293 }
294  
295 public bool DearchiveInventory(
296 UUID id, string firstName, string lastName, string invPath, Stream loadStream,
297 Dictionary<string, object> options)
298 {
299 if (m_scenes.Count > 0)
300 {
301 UserAccount userInfo = GetUserInfo(firstName, lastName);
302  
303 if (userInfo != null)
304 {
305 // if (CheckPresence(userInfo.PrincipalID))
306 // {
307 InventoryArchiveReadRequest request;
308 bool merge = (options.ContainsKey("merge") ? (bool)options["merge"] : false);
309  
310 try
311 {
312 request = new InventoryArchiveReadRequest(id, m_aScene.InventoryService, this, m_aScene.AssetService, m_aScene.UserAccountService, userInfo, invPath, loadStream, merge);
313 }
314 catch (EntryPointNotFoundException e)
315 {
316 m_log.ErrorFormat(
317 "[INVENTORY ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
318 + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
319 m_log.Error(e);
320  
321 return false;
322 }
323  
324 UpdateClientWithLoadedNodes(userInfo, request.Execute());
325  
326 return true;
327 // }
328 // else
329 // {
330 // m_log.ErrorFormat(
331 // "[INVENTORY ARCHIVER]: User {0} {1} {2} not logged in to this region simulator",
332 // userInfo.FirstName, userInfo.LastName, userInfo.PrincipalID);
333 // }
334 }
335 else
336 m_log.ErrorFormat("[INVENTORY ARCHIVER]: User {0} {1} not found",
337 firstName, lastName);
338 }
339  
340 return false;
341 }
342  
343 public bool DearchiveInventory(
344 UUID id, string firstName, string lastName, string invPath, string loadPath,
345 Dictionary<string, object> options)
346 {
347 if (m_scenes.Count > 0)
348 {
349 UserAccount userInfo = GetUserInfo(firstName, lastName);
350  
351 if (userInfo != null)
352 {
353 // if (CheckPresence(userInfo.PrincipalID))
354 // {
355 InventoryArchiveReadRequest request;
356 bool merge = (options.ContainsKey("merge") ? (bool)options["merge"] : false);
357  
358 try
359 {
360 request = new InventoryArchiveReadRequest(id, m_aScene.InventoryService, this, m_aScene.AssetService, m_aScene.UserAccountService, userInfo, invPath, loadPath, merge);
361 }
362 catch (EntryPointNotFoundException e)
363 {
364 m_log.ErrorFormat(
365 "[INVENTORY ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
366 + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
367 m_log.Error(e);
368  
369 return false;
370 }
371  
372 UpdateClientWithLoadedNodes(userInfo, request.Execute());
373  
374 return true;
375 // }
376 // else
377 // {
378 // m_log.ErrorFormat(
379 // "[INVENTORY ARCHIVER]: User {0} {1} {2} not logged in to this region simulator",
380 // userInfo.FirstName, userInfo.LastName, userInfo.PrincipalID);
381 // }
382 }
383 }
384  
385 return false;
386 }
387  
388 /// <summary>
389 /// Load inventory from an inventory file archive
390 /// </summary>
391 /// <param name="cmdparams"></param>
392 protected void HandleLoadInvConsoleCommand(string module, string[] cmdparams)
393 {
394 try
395 {
396 UUID id = UUID.Random();
397  
398 Dictionary<string, object> options = new Dictionary<string, object>();
399 OptionSet optionSet = new OptionSet().Add("m|merge", delegate (string v) { options["merge"] = v != null; });
400  
401 List<string> mainParams = optionSet.Parse(cmdparams);
402  
403 if (mainParams.Count < 5)
404 {
405 m_log.Error(
406 "[INVENTORY ARCHIVER]: usage is load iar [-m|--merge] <first name> <last name> <inventory path> [<load file path>]");
407 return;
408 }
409  
410 string firstName = mainParams[2];
411 string lastName = mainParams[3];
412 string invPath = mainParams[4];
413 string loadPath = (mainParams.Count > 5 ? mainParams[5] : DEFAULT_INV_BACKUP_FILENAME);
414  
415 m_log.InfoFormat(
416 "[INVENTORY ARCHIVER]: Loading archive {0} to inventory path {1} for {2} {3}",
417 loadPath, invPath, firstName, lastName);
418  
419 lock (m_pendingConsoleTasks)
420 m_pendingConsoleTasks.Add(id);
421  
422 DearchiveInventory(id, firstName, lastName, invPath, loadPath, options);
423 }
424 catch (InventoryArchiverException e)
425 {
426 m_log.ErrorFormat("[INVENTORY ARCHIVER]: {0}", e.Message);
427 }
428 }
429  
430 /// <summary>
431 /// Save inventory to a file archive
432 /// </summary>
433 /// <param name="cmdparams"></param>
434 protected void HandleSaveInvConsoleCommand(string module, string[] cmdparams)
435 {
436 UUID id = UUID.Random();
437  
438 Dictionary<string, object> options = new Dictionary<string, object>();
439  
440 OptionSet ops = new OptionSet();
441 //ops.Add("v|version=", delegate(string v) { options["version"] = v; });
442 ops.Add("h|home=", delegate(string v) { options["home"] = v; });
443 ops.Add("v|verbose", delegate(string v) { options["verbose"] = v; });
444 ops.Add("c|creators", delegate(string v) { options["creators"] = v; });
445 ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; });
446 ops.Add("e|exclude=", delegate(string v)
447 {
448 if (!options.ContainsKey("exclude"))
449 options["exclude"] = new List<String>();
450 ((List<String>)options["exclude"]).Add(v);
451 });
452 ops.Add("f|excludefolder=", delegate(string v)
453 {
454 if (!options.ContainsKey("excludefolders"))
455 options["excludefolders"] = new List<String>();
456 ((List<String>)options["excludefolders"]).Add(v);
457 });
458 ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; });
459  
460 List<string> mainParams = ops.Parse(cmdparams);
461  
462 try
463 {
464 if (mainParams.Count < 5)
465 {
466 m_log.Error(
467 "[INVENTORY ARCHIVER]: save iar [-h|--home=<url>] [--noassets] <first> <last> <inventory path> [<IAR path>] [-c|--creators] [-e|--exclude=<name/uuid>] [-f|--excludefolder=<foldername/uuid>] [-v|--verbose]");
468 return;
469 }
470  
471 if (options.ContainsKey("home"))
472 m_log.WarnFormat("[INVENTORY ARCHIVER]: Please be aware that inventory archives with creator information are not compatible with OpenSim 0.7.0.2 and earlier. Do not use the -home option if you want to produce a compatible IAR");
473  
474 string firstName = mainParams[2];
475 string lastName = mainParams[3];
476 string invPath = mainParams[4];
477 string savePath = (mainParams.Count > 5 ? mainParams[5] : DEFAULT_INV_BACKUP_FILENAME);
478  
479 m_log.InfoFormat(
480 "[INVENTORY ARCHIVER]: Saving archive {0} using inventory path {1} for {2} {3}",
481 savePath, invPath, firstName, lastName);
482  
483 lock (m_pendingConsoleTasks)
484 m_pendingConsoleTasks.Add(id);
485  
486 ArchiveInventory(id, firstName, lastName, invPath, savePath, options);
487 }
488 catch (InventoryArchiverException e)
489 {
490 m_log.ErrorFormat("[INVENTORY ARCHIVER]: {0}", e.Message);
491 }
492 }
493  
494 private void SaveInvConsoleCommandCompleted(
495 UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream,
496 Exception reportedException, int SaveCount, int FilterCount)
497 {
498 lock (m_pendingConsoleTasks)
499 {
500 if (m_pendingConsoleTasks.Contains(id))
501 m_pendingConsoleTasks.Remove(id);
502 else
503 return;
504 }
505  
506 if (succeeded)
507 {
508 // Report success and include item count and filter count (Skipped items due to --perm or --exclude switches)
509 if(FilterCount == 0)
510 m_log.InfoFormat("[INVENTORY ARCHIVER]: Saved archive with {0} items for {1} {2}", SaveCount, userInfo.FirstName, userInfo.LastName);
511 else
512 m_log.InfoFormat("[INVENTORY ARCHIVER]: Saved archive with {0} items for {1} {2}. Skipped {3} items due to exclude and/or perm switches", SaveCount, userInfo.FirstName, userInfo.LastName, FilterCount);
513 }
514 else
515 {
516 m_log.ErrorFormat(
517 "[INVENTORY ARCHIVER]: Archive save for {0} {1} failed - {2}",
518 userInfo.FirstName, userInfo.LastName, reportedException.Message);
519 }
520 }
521  
522 private void LoadInvConsoleCommandCompleted(
523 UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream loadStream,
524 Exception reportedException, int LoadCount)
525 {
526 lock (m_pendingConsoleTasks)
527 {
528 if (m_pendingConsoleTasks.Contains(id))
529 m_pendingConsoleTasks.Remove(id);
530 else
531 return;
532 }
533  
534 if (succeeded)
535 {
536 m_log.InfoFormat("[INVENTORY ARCHIVER]: Loaded {0} items from archive {1} for {2} {3}", LoadCount, invPath, userInfo.FirstName, userInfo.LastName);
537 }
538 else
539 {
540 m_log.ErrorFormat(
541 "[INVENTORY ARCHIVER]: Archive load for {0} {1} failed - {2}",
542 userInfo.FirstName, userInfo.LastName, reportedException.Message);
543 }
544 }
545  
546 /// <summary>
547 /// Get user information for the given name.
548 /// </summary>
549 /// <param name="firstName"></param>
550 /// <param name="lastName"></param>
551 /// <param name="pass">User password</param>
552 /// <returns></returns>
553 protected UserAccount GetUserInfo(string firstName, string lastName)
554 {
555 UserAccount account
556 = m_aScene.UserAccountService.GetUserAccount(m_aScene.RegionInfo.ScopeID, firstName, lastName);
557  
558 if (null == account)
559 {
560 m_log.ErrorFormat(
561 "[INVENTORY ARCHIVER]: Failed to find user info for {0} {1}",
562 firstName, lastName);
563 return null;
564 }
565  
566 return account;
567 }
568  
569 /// <summary>
570 /// Notify the client of loaded nodes if they are logged in
571 /// </summary>
572 /// <param name="loadedNodes">Can be empty. In which case, nothing happens</param>
573 private void UpdateClientWithLoadedNodes(UserAccount userInfo, HashSet<InventoryNodeBase> loadedNodes)
574 {
575 if (loadedNodes.Count == 0)
576 return;
577  
578 foreach (Scene scene in m_scenes.Values)
579 {
580 ScenePresence user = scene.GetScenePresence(userInfo.PrincipalID);
581  
582 if (user != null && !user.IsChildAgent)
583 {
584 foreach (InventoryNodeBase node in loadedNodes)
585 {
586 // m_log.DebugFormat(
587 // "[INVENTORY ARCHIVER]: Notifying {0} of loaded inventory node {1}",
588 // user.Name, node.Name);
589  
590 user.ControllingClient.SendBulkUpdateInventory(node);
591 }
592  
593 break;
594 }
595 }
596 }
597  
598 // /// <summary>
599 // /// Check if the given user is present in any of the scenes.
600 // /// </summary>
601 // /// <param name="userId">The user to check</param>
602 // /// <returns>true if the user is in any of the scenes, false otherwise</returns>
603 // protected bool CheckPresence(UUID userId)
604 // {
605 // if (DisablePresenceChecks)
606 // return true;
607 //
608 // foreach (Scene scene in m_scenes.Values)
609 // {
610 // ScenePresence p;
611 // if ((p = scene.GetScenePresence(userId)) != null)
612 // {
613 // p.ControllingClient.SendAgentAlertMessage("Inventory operation has been started", false);
614 // return true;
615 // }
616 // }
617 //
618 // return false;
619 // }
620 }
621 }