/// <summary> /// Create an archive item name given its constituent components /// </summary> /// <param name="name"></param> /// <param name="id"></param> /// <returns></returns> public static string CreateArchiveItemName(string name, UUID id) { return(string.Format( "{0}{1}{2}.xml", InventoryArchiveUtils.EscapeArchivePath(name), ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR, id)); }
/// <summary> /// Resolve a destination folder /// </summary> /// We require here a root destination folder (usually the root of the user's inventory) and the archive /// path. We also pass in a list of previously resolved folders in case we've found this one previously. /// <param name = "archivePath"> /// The item archive path to resolve. The portion of the path passed back is that /// which corresponds to the resolved desintation folder. /// <param name = "rootDestinationFolder"> /// The root folder for the inventory load /// </param> /// <param name = "resolvedFolders"> /// The folders that we have resolved so far for a given archive path. /// </param> /// <returns> /// The folder in the user's inventory that matches best the archive path given. If no such folder was found /// then the passed in root destination folder is returned. /// </returns> protected InventoryFolderBase ResolveDestinationFolder( InventoryFolderBase rootDestFolder, ref string archivePath, ref Dictionary <string, InventoryFolderBase> resolvedFolders) { // string originalArchivePath = archivePath; while (archivePath.Length > 0) { // MainConsole.Instance.DebugFormat("[INVENTORY ARCHIVER]: Trying to resolve destination folder {0}", archivePath); if (resolvedFolders.ContainsKey(archivePath)) { // MainConsole.Instance.DebugFormat( // "[INVENTORY ARCHIVER]: Found previously created folder from archive path {0}", archivePath); return(resolvedFolders[archivePath]); } else { if (m_merge) { // TODO: Using m_invPath is totally wrong - what we need to do is strip the uuid from the // iar name and try to find that instead. string plainPath = ArchiveConstants.ExtractPlainPathFromIarPath(archivePath); List <InventoryFolderBase> folderCandidates = InventoryArchiveUtils.FindFolderByPath( m_registry.RequestModuleInterface <IInventoryService>(), m_userInfo.PrincipalID, plainPath); if (folderCandidates.Count != 0) { InventoryFolderBase destFolder = folderCandidates[0]; resolvedFolders[archivePath] = destFolder; return(destFolder); } } // Don't include the last slash so find the penultimate one int penultimateSlashIndex = archivePath.LastIndexOf("/", archivePath.Length - 2); if (penultimateSlashIndex >= 0) { // Remove the last section of path so that we can see if we've already resolved the parent archivePath = archivePath.Remove(penultimateSlashIndex + 1); } else { // MainConsole.Instance.DebugFormat( // "[INVENTORY ARCHIVER]: Found no previously created folder for archive path {0}", // originalArchivePath); archivePath = string.Empty; return(rootDestFolder); } } } return(rootDestFolder); }
/// <summary> /// Create a set of folders for the given path. /// </summary> /// <param name="destFolder"> /// The root folder from which the creation will take place. /// </param> /// <param name="iarPathExisting"> /// the part of the iar path that already exists /// </param> /// <param name="iarPathToReplicate"> /// The path to replicate in the user's inventory from iar /// </param> /// <param name="resolvedFolders"> /// The folders that we have resolved so far for a given archive path. /// </param> /// <param name="loadedNodes"> /// Track the inventory nodes created. /// </param> protected void CreateFoldersForPath( InventoryFolderBase destFolder, string iarPathExisting, string iarPathToReplicate, ref Dictionary <string, InventoryFolderBase> resolvedFolders, ref HashSet <InventoryNodeBase> loadedNodes) { string[] rawDirsToCreate = iarPathToReplicate.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < rawDirsToCreate.Length; i++) { // MainConsole.Instance.DebugFormat("[INVENTORY ARCHIVER]: Creating folder {0} from IAR", rawDirsToCreate[i]); if (!rawDirsToCreate[i].Contains(ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR)) { continue; } int identicalNameIdentifierIndex = rawDirsToCreate[i].LastIndexOf( ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR); string newFolderName = rawDirsToCreate[i].Remove(identicalNameIdentifierIndex); newFolderName = InventoryArchiveUtils.UnescapeArchivePath(newFolderName); UUID newFolderId = UUID.Random(); // Asset type has to be Unknown here rather than Folder, otherwise the created folder can't be // deleted once the client has relogged. // The root folder appears to be labelled AssetType.Folder (shows up as "Category" in the client) // even though there is a AssetType.RootCategory destFolder = new InventoryFolderBase( newFolderId, newFolderName, m_userInfo.PrincipalID, (short)AssetType.Unknown, destFolder.ID, 1); m_registry.RequestModuleInterface <IInventoryService>().AddFolder(destFolder); // Record that we have now created this folder iarPathExisting += rawDirsToCreate[i] + "/"; MainConsole.Instance.DebugFormat("[INVENTORY ARCHIVER]: Created folder {0} from IAR", iarPathExisting); resolvedFolders[iarPathExisting] = destFolder; if (0 == i && loadedNodes != null) { loadedNodes.Add(destFolder); } } }
/// <summary> /// Execute the request /// </summary> /// <returns> /// A list of the inventory nodes loaded. If folders were loaded then only the root folders are /// returned /// </returns> public HashSet <InventoryNodeBase> Execute(bool loadAll) { if (m_loadStream == null) { return(new HashSet <InventoryNodeBase>()); } try { string filePath = "ERROR"; int successfulAssetRestores = 0; int failedAssetRestores = 0; int successfulItemRestores = 0; HashSet <InventoryNodeBase> loadedNodes = loadAll ? new HashSet <InventoryNodeBase>() : null; List <InventoryFolderBase> folderCandidates = InventoryArchiveUtils.FindFolderByPath( m_registry.RequestModuleInterface <IInventoryService>(), m_userInfo.PrincipalID, m_invPath); if (folderCandidates.Count == 0) { // Possibly provide an option later on to automatically create this folder if it does not exist MainConsole.Instance.ErrorFormat("[INVENTORY ARCHIVER]: Inventory path {0} does not exist", m_invPath); return(loadedNodes); } InventoryFolderBase rootDestinationFolder = folderCandidates[0]; archive = new TarArchiveReader(m_loadStream); // In order to load identically named folders, we need to keep track of the folders that we have already // resolved Dictionary <string, InventoryFolderBase> resolvedFolders = new Dictionary <string, InventoryFolderBase>(); byte[] data; TarArchiveReader.TarEntryType entryType; while ((data = archive.ReadEntry(out filePath, out entryType)) != null) { if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) { if (LoadAsset(filePath, data)) { successfulAssetRestores++; } else { failedAssetRestores++; } if ((successfulAssetRestores) % 50 == 0) { MainConsole.Instance.InfoFormat( "[INVENTORY ARCHIVER]: Loaded {0} assets...", successfulAssetRestores); } } else if (filePath.StartsWith(ArchiveConstants.INVENTORY_PATH)) { filePath = filePath.Substring(ArchiveConstants.INVENTORY_PATH.Length); // Trim off the file portion if we aren't already dealing with a directory path if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY != entryType) { filePath = filePath.Remove(filePath.LastIndexOf("/") + 1); } InventoryFolderBase foundFolder = ReplicateArchivePathToUserInventory( filePath, rootDestinationFolder, ref resolvedFolders, ref loadedNodes); if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY != entryType) { InventoryItemBase item = LoadItem(data, foundFolder); if (item != null) { successfulItemRestores++; if ((successfulItemRestores) % 50 == 0) { MainConsole.Instance.InfoFormat( "[INVENTORY ARCHIVER]: Restored {0} items...", successfulItemRestores); } // If we aren't loading the folder containing the item then well need to update the // viewer separately for that item. if (loadAll && !loadedNodes.Contains(foundFolder)) { loadedNodes.Add(item); } } item = null; } } data = null; } int successfulItemLoaded = 0; foreach (InventoryItemBase item in itemsSavedOff) { AddInventoryItem(item); successfulItemLoaded++; if ((successfulItemLoaded) % 50 == 0) { MainConsole.Instance.InfoFormat( "[INVENTORY ARCHIVER]: Loaded {0} items of {1}...", successfulItemLoaded, itemsSavedOff.Count); } } itemsSavedOff.Clear(); assets2Save.Clear(); MainConsole.Instance.InfoFormat( "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures", successfulAssetRestores, failedAssetRestores); MainConsole.Instance.InfoFormat("[INVENTORY ARCHIVER]: Successfully loaded {0} items", successfulItemRestores); return(loadedNodes); } finally { m_loadStream.Close(); } }
/// <summary> /// Execute the inventory write request /// </summary> public void Execute() { try { InventoryFolderBase inventoryFolder = null; InventoryItemBase inventoryItem = null; InventoryFolderBase rootFolder = m_registry.RequestModuleInterface <IInventoryService>().GetRootFolder(m_userInfo.PrincipalID); if (m_defaultFolderToSave != null) { rootFolder = m_defaultFolderToSave; } bool saveFolderContentsOnly = false; // Eliminate double slashes and any leading / on the path. string[] components = m_invPath.Split( new[] { InventoryFolderImpl.PATH_DELIMITER }, StringSplitOptions.RemoveEmptyEntries); int maxComponentIndex = components.Length - 1; // If the path terminates with a STAR then later on we want to archive all nodes in the folder but not the // folder itself. This may get more sophisicated later on if (maxComponentIndex >= 0 && components[maxComponentIndex] == STAR_WILDCARD) { saveFolderContentsOnly = true; maxComponentIndex--; } m_invPath = String.Empty; for (int i = 0; i <= maxComponentIndex; i++) { m_invPath += components[i] + InventoryFolderImpl.PATH_DELIMITER; } // Annoyingly Split actually returns the original string if the input string consists only of delimiters // Therefore if we still start with a / after the split, then we need the root folder if (m_invPath.Length == 0) { inventoryFolder = rootFolder; } else { m_invPath = m_invPath.Remove(m_invPath.LastIndexOf(InventoryFolderImpl.PATH_DELIMITER)); List <InventoryFolderBase> candidateFolders = InventoryArchiveUtils.FindFolderByPath( m_registry.RequestModuleInterface <IInventoryService>(), rootFolder, m_invPath); if (candidateFolders.Count > 0) { inventoryFolder = candidateFolders[0]; } } // The path may point to an item instead if (inventoryFolder == null) { inventoryItem = InventoryArchiveUtils.FindItemByPath(m_registry.RequestModuleInterface <IInventoryService>(), rootFolder, m_invPath); //inventoryItem = m_userInfo.RootFolder.FindItemByPath(m_invPath); } if (null == inventoryFolder && null == inventoryItem) { // We couldn't find the path indicated string errorMessage = string.Format("Aborted save. Could not find inventory path {0}", m_invPath); Exception e = new InventoryArchiverException(errorMessage); if (m_module != null) { m_module.TriggerInventoryArchiveSaved(m_id, false, m_userInfo, m_invPath, m_saveStream, e); } throw e; } m_archiveWriter = new TarArchiveWriter(m_saveStream); if (inventoryFolder != null) { MainConsole.Instance.DebugFormat( "[INVENTORY ARCHIVER]: Found folder {0} {1} at {2}", inventoryFolder.Name, inventoryFolder.ID, m_invPath == String.Empty ? InventoryFolderImpl.PATH_DELIMITER : m_invPath); //recurse through all dirs getting dirs and files SaveInvFolder(inventoryFolder, ArchiveConstants.INVENTORY_PATH, !saveFolderContentsOnly); } else if (inventoryItem != null) { MainConsole.Instance.DebugFormat( "[INVENTORY ARCHIVER]: Found item {0} {1} at {2}", inventoryItem.Name, inventoryItem.ID, m_invPath); SaveInvItem(inventoryItem, ArchiveConstants.INVENTORY_PATH); } // Don't put all this profile information into the archive right now. //SaveUsers(); } catch (Exception) { m_saveStream.Close(); throw; } if (m_saveAssets) { foreach (AssetBase asset in m_assetsToAdd) { m_assetUuids[asset.ID] = (AssetType)asset.Type; } new AssetsRequest( new AssetsArchiver(m_archiveWriter), m_assetUuids, m_registry.RequestModuleInterface <IAssetService>(), ReceivedAllAssets).Execute(); } else { MainConsole.Instance.Debug("[INVENTORY ARCHIVER]: Save Complete"); m_archiveWriter.Close(); } }