diff options
author | Dr Scofield | 2009-02-10 13:10:57 +0000 |
---|---|---|
committer | Dr Scofield | 2009-02-10 13:10:57 +0000 |
commit | 180be7de07014aa33bc6066f12a0819b731c1c9d (patch) | |
tree | 3aa13af3cda4b808fa9453655875327699b61311 /OpenSim/Region/CoreModules/World/Archiver | |
parent | Stopgap measure: To use gridlaunch, or GUI, start opensim with (diff) | |
download | opensim-SC-180be7de07014aa33bc6066f12a0819b731c1c9d.zip opensim-SC-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.gz opensim-SC-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.bz2 opensim-SC-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.xz |
this is step 2 of 2 of the OpenSim.Region.Environment refactor.
NOTHING has been deleted or moved off to forge at this point. what
has happened is that OpenSim.Region.Environment.Modules has been split
in two:
- OpenSim.Region.CoreModules: all those modules that are either
directly or indirectly referenced from other OpenSim packages, or
that provide functionality that the OpenSim developer community
considers core functionality:
CoreModules/Agent/AssetTransaction
CoreModules/Agent/Capabilities
CoreModules/Agent/TextureDownload
CoreModules/Agent/TextureSender
CoreModules/Agent/TextureSender/Tests
CoreModules/Agent/Xfer
CoreModules/Avatar/AvatarFactory
CoreModules/Avatar/Chat/ChatModule
CoreModules/Avatar/Combat
CoreModules/Avatar/Currency/SampleMoney
CoreModules/Avatar/Dialog
CoreModules/Avatar/Friends
CoreModules/Avatar/Gestures
CoreModules/Avatar/Groups
CoreModules/Avatar/InstantMessage
CoreModules/Avatar/Inventory
CoreModules/Avatar/Inventory/Archiver
CoreModules/Avatar/Inventory/Transfer
CoreModules/Avatar/Lure
CoreModules/Avatar/ObjectCaps
CoreModules/Avatar/Profiles
CoreModules/Communications/Local
CoreModules/Communications/REST
CoreModules/Framework/EventQueue
CoreModules/Framework/InterfaceCommander
CoreModules/Hypergrid
CoreModules/InterGrid
CoreModules/Scripting/DynamicTexture
CoreModules/Scripting/EMailModules
CoreModules/Scripting/HttpRequest
CoreModules/Scripting/LoadImageURL
CoreModules/Scripting/VectorRender
CoreModules/Scripting/WorldComm
CoreModules/Scripting/XMLRPC
CoreModules/World/Archiver
CoreModules/World/Archiver/Tests
CoreModules/World/Estate
CoreModules/World/Land
CoreModules/World/Permissions
CoreModules/World/Serialiser
CoreModules/World/Sound
CoreModules/World/Sun
CoreModules/World/Terrain
CoreModules/World/Terrain/DefaultEffects
CoreModules/World/Terrain/DefaultEffects/bin
CoreModules/World/Terrain/DefaultEffects/bin/Debug
CoreModules/World/Terrain/Effects
CoreModules/World/Terrain/FileLoaders
CoreModules/World/Terrain/FloodBrushes
CoreModules/World/Terrain/PaintBrushes
CoreModules/World/Terrain/Tests
CoreModules/World/Vegetation
CoreModules/World/Wind
CoreModules/World/WorldMap
- OpenSim.Region.OptionalModules: all those modules that are not core
modules:
OptionalModules/Avatar/Chat/IRC-stuff
OptionalModules/Avatar/Concierge
OptionalModules/Avatar/Voice/AsterixVoice
OptionalModules/Avatar/Voice/SIPVoice
OptionalModules/ContentManagementSystem
OptionalModules/Grid/Interregion
OptionalModules/Python
OptionalModules/SvnSerialiser
OptionalModules/World/NPC
OptionalModules/World/TreePopulator
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Archiver')
12 files changed, 2485 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs new file mode 100644 index 0000000..179d1a2 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs | |||
@@ -0,0 +1,128 @@ | |||
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 OpenSim 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.Collections.Generic; | ||
29 | using OpenMetaverse; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
32 | { | ||
33 | /// <summary> | ||
34 | /// Constants for the archiving module | ||
35 | /// </summary> | ||
36 | public class ArchiveConstants | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// The location of the archive control file | ||
40 | /// </summary> | ||
41 | public static readonly string CONTROL_FILE_PATH = "archive.xml"; | ||
42 | |||
43 | /// <summary> | ||
44 | /// Path for the assets held in an archive | ||
45 | /// </summary> | ||
46 | public static readonly string ASSETS_PATH = "assets/"; | ||
47 | |||
48 | /// <summary> | ||
49 | /// Path for the assets metadata file | ||
50 | /// </summary> | ||
51 | //public static readonly string ASSETS_METADATA_PATH = "assets.xml"; | ||
52 | |||
53 | /// <summary> | ||
54 | /// Path for the prims file | ||
55 | /// </summary> | ||
56 | public static readonly string OBJECTS_PATH = "objects/"; | ||
57 | |||
58 | /// <summary> | ||
59 | /// Path for terrains. Technically these may be assets, but I think it's quite nice to split them out. | ||
60 | /// </summary> | ||
61 | public static readonly string TERRAINS_PATH = "terrains/"; | ||
62 | |||
63 | /// <summary> | ||
64 | /// Path for region settings. | ||
65 | /// </summary> | ||
66 | public static readonly string SETTINGS_PATH = "settings/"; | ||
67 | |||
68 | /// <summary> | ||
69 | /// The character the separates the uuid from extension information in an archived asset filename | ||
70 | /// </summary> | ||
71 | public static readonly string ASSET_EXTENSION_SEPARATOR = "_"; | ||
72 | |||
73 | /// <summary> | ||
74 | /// Extensions used for asset types in the archive | ||
75 | /// </summary> | ||
76 | public static readonly IDictionary<sbyte, string> ASSET_TYPE_TO_EXTENSION = new Dictionary<sbyte, string>(); | ||
77 | public static readonly IDictionary<string, sbyte> EXTENSION_TO_ASSET_TYPE = new Dictionary<string, sbyte>(); | ||
78 | |||
79 | static ArchiveConstants() | ||
80 | { | ||
81 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Animation] = ASSET_EXTENSION_SEPARATOR + "animation.bvh"; | ||
82 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Bodypart] = ASSET_EXTENSION_SEPARATOR + "bodypart.txt"; | ||
83 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.CallingCard] = ASSET_EXTENSION_SEPARATOR + "callingcard.txt"; | ||
84 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Clothing] = ASSET_EXTENSION_SEPARATOR + "clothing.txt"; | ||
85 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Folder] = ASSET_EXTENSION_SEPARATOR + "folder.txt"; // Not sure if we'll ever see this | ||
86 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Gesture] = ASSET_EXTENSION_SEPARATOR + "gesture.txt"; | ||
87 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageJPEG] = ASSET_EXTENSION_SEPARATOR + "image.jpg"; | ||
88 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageTGA] = ASSET_EXTENSION_SEPARATOR + "image.tga"; | ||
89 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Landmark] = ASSET_EXTENSION_SEPARATOR + "landmark.txt"; | ||
90 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LostAndFoundFolder] = ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"; // Not sure if we'll ever see this | ||
91 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLBytecode] = ASSET_EXTENSION_SEPARATOR + "bytecode.lso"; | ||
92 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLText] = ASSET_EXTENSION_SEPARATOR + "script.lsl"; | ||
93 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Notecard] = ASSET_EXTENSION_SEPARATOR + "notecard.txt"; | ||
94 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Object] = ASSET_EXTENSION_SEPARATOR + "object.xml"; | ||
95 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.RootFolder] = ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"; // Not sure if we'll ever see this | ||
96 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Simstate] = ASSET_EXTENSION_SEPARATOR + "simstate.bin"; // Not sure if we'll ever see this | ||
97 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SnapshotFolder] = ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"; // Not sure if we'll ever see this | ||
98 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Sound] = ASSET_EXTENSION_SEPARATOR + "sound.ogg"; | ||
99 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV] = ASSET_EXTENSION_SEPARATOR + "sound.wav"; | ||
100 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Texture] = ASSET_EXTENSION_SEPARATOR + "texture.jp2"; | ||
101 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TextureTGA] = ASSET_EXTENSION_SEPARATOR + "texture.tga"; | ||
102 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TrashFolder] = ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"; // Not sure if we'll ever see this | ||
103 | |||
104 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"] = (sbyte)AssetType.Animation; | ||
105 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"] = (sbyte)AssetType.Bodypart; | ||
106 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "callingcard.txt"] = (sbyte)AssetType.CallingCard; | ||
107 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "clothing.txt"] = (sbyte)AssetType.Clothing; | ||
108 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "folder.txt"] = (sbyte)AssetType.Folder; | ||
109 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "gesture.txt"] = (sbyte)AssetType.Gesture; | ||
110 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.jpg"] = (sbyte)AssetType.ImageJPEG; | ||
111 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.tga"] = (sbyte)AssetType.ImageTGA; | ||
112 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "landmark.txt"] = (sbyte)AssetType.Landmark; | ||
113 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"] = (sbyte)AssetType.LostAndFoundFolder; | ||
114 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bytecode.lso"] = (sbyte)AssetType.LSLBytecode; | ||
115 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "script.lsl"] = (sbyte)AssetType.LSLText; | ||
116 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "notecard.txt"] = (sbyte)AssetType.Notecard; | ||
117 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "object.xml"] = (sbyte)AssetType.Object; | ||
118 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"] = (sbyte)AssetType.RootFolder; | ||
119 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "simstate.bin"] = (sbyte)AssetType.Simstate; | ||
120 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"] = (sbyte)AssetType.SnapshotFolder; | ||
121 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.ogg"] = (sbyte)AssetType.Sound; | ||
122 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.wav"] = (sbyte)AssetType.SoundWAV; | ||
123 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"] = (sbyte)AssetType.Texture; | ||
124 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = (sbyte)AssetType.TextureTGA; | ||
125 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder; | ||
126 | } | ||
127 | } | ||
128 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs new file mode 100644 index 0000000..3218abc --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs | |||
@@ -0,0 +1,460 @@ | |||
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 OpenSim 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.IO.Compression; | ||
32 | using System.Reflection; | ||
33 | using System.Xml; | ||
34 | using System.Net; | ||
35 | using OpenMetaverse; | ||
36 | using log4net; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Framework.Communications.Cache; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | using OpenSim.Region.CoreModules.World.Terrain; | ||
42 | |||
43 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
44 | { | ||
45 | /// <summary> | ||
46 | /// Handles an individual archive read request | ||
47 | /// </summary> | ||
48 | public class ArchiveReadRequest | ||
49 | { | ||
50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
51 | |||
52 | private static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding(); | ||
53 | |||
54 | private Scene m_scene; | ||
55 | private Stream m_loadStream; | ||
56 | private string m_errorMessage; | ||
57 | |||
58 | /// <summary> | ||
59 | /// Used to cache lookups for valid uuids. | ||
60 | /// </summary> | ||
61 | private IDictionary<UUID, bool> m_validUserUuids = new Dictionary<UUID, bool>(); | ||
62 | |||
63 | public ArchiveReadRequest(Scene scene, string loadPath) | ||
64 | { | ||
65 | m_scene = scene; | ||
66 | m_loadStream = new GZipStream(GetStream(loadPath), CompressionMode.Decompress); | ||
67 | m_errorMessage = String.Empty; | ||
68 | } | ||
69 | |||
70 | public ArchiveReadRequest(Scene scene, Stream loadStream) | ||
71 | { | ||
72 | m_scene = scene; | ||
73 | m_loadStream = loadStream; | ||
74 | } | ||
75 | |||
76 | /// <summary> | ||
77 | /// Dearchive the region embodied in this request. | ||
78 | /// </summary> | ||
79 | public void DearchiveRegion() | ||
80 | { | ||
81 | // The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions | ||
82 | DearchiveRegion0DotStar(); | ||
83 | } | ||
84 | |||
85 | private void DearchiveRegion0DotStar() | ||
86 | { | ||
87 | int successfulAssetRestores = 0; | ||
88 | int failedAssetRestores = 0; | ||
89 | List<string> serialisedSceneObjects = new List<string>(); | ||
90 | |||
91 | try | ||
92 | { | ||
93 | TarArchiveReader archive = new TarArchiveReader(m_loadStream); | ||
94 | |||
95 | //AssetsDearchiver dearchiver = new AssetsDearchiver(m_scene.AssetCache); | ||
96 | |||
97 | string filePath = "ERROR"; | ||
98 | |||
99 | byte[] data; | ||
100 | TarArchiveReader.TarEntryType entryType; | ||
101 | |||
102 | while ((data = archive.ReadEntry(out filePath, out entryType)) != null) | ||
103 | { | ||
104 | //m_log.DebugFormat( | ||
105 | // "[ARCHIVER]: Successfully read {0} ({1} bytes)}", filePath, data.Length); | ||
106 | if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType) | ||
107 | { | ||
108 | m_log.WarnFormat("[ARCHIVER]: Ignoring directory entry {0}", | ||
109 | filePath); | ||
110 | } | ||
111 | else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) | ||
112 | { | ||
113 | serialisedSceneObjects.Add(m_asciiEncoding.GetString(data)); | ||
114 | } | ||
115 | // else if (filePath.Equals(ArchiveConstants.ASSETS_METADATA_PATH)) | ||
116 | // { | ||
117 | // string xml = m_asciiEncoding.GetString(data); | ||
118 | // dearchiver.AddAssetMetadata(xml); | ||
119 | // } | ||
120 | else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) | ||
121 | { | ||
122 | if (LoadAsset(filePath, data)) | ||
123 | successfulAssetRestores++; | ||
124 | else | ||
125 | failedAssetRestores++; | ||
126 | } | ||
127 | else if (filePath.StartsWith(ArchiveConstants.TERRAINS_PATH)) | ||
128 | { | ||
129 | LoadTerrain(filePath, data); | ||
130 | } | ||
131 | else if (filePath.StartsWith(ArchiveConstants.SETTINGS_PATH)) | ||
132 | { | ||
133 | LoadRegionSettings(filePath, data); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | //m_log.Debug("[ARCHIVER]: Reached end of archive"); | ||
138 | |||
139 | archive.Close(); | ||
140 | } | ||
141 | catch (Exception e) | ||
142 | { | ||
143 | m_log.ErrorFormat( | ||
144 | "[ARCHIVER]: Error loading oar file. Exception was: {0}", e); | ||
145 | m_errorMessage += e.ToString(); | ||
146 | m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage); | ||
147 | return; | ||
148 | } | ||
149 | |||
150 | m_log.InfoFormat("[ARCHIVER]: Restored {0} assets", successfulAssetRestores); | ||
151 | |||
152 | if (failedAssetRestores > 0) | ||
153 | { | ||
154 | m_log.ErrorFormat("[ARCHIVER]: Failed to load {0} assets", failedAssetRestores); | ||
155 | m_errorMessage += String.Format("Failed to load {0} assets", failedAssetRestores); | ||
156 | } | ||
157 | |||
158 | m_log.Info("[ARCHIVER]: Clearing all existing scene objects"); | ||
159 | m_scene.DeleteAllSceneObjects(); | ||
160 | |||
161 | // Reload serialized prims | ||
162 | m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count); | ||
163 | |||
164 | IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface<IRegionSerialiserModule>(); | ||
165 | ICollection<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | ||
166 | |||
167 | foreach (string serialisedSceneObject in serialisedSceneObjects) | ||
168 | { | ||
169 | SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject); | ||
170 | |||
171 | // For now, give all incoming scene objects new uuids. This will allow scenes to be cloned | ||
172 | // on the same region server and multiple examples a single object archive to be imported | ||
173 | // to the same scene (when this is possible). | ||
174 | sceneObject.ResetIDs(); | ||
175 | |||
176 | // Try to retain the original creator/owner/lastowner if their uuid is present on this grid | ||
177 | // otherwise, use the master avatar uuid instead | ||
178 | UUID masterAvatarId = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
179 | |||
180 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
181 | masterAvatarId = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
182 | |||
183 | foreach (SceneObjectPart part in sceneObject.Children.Values) | ||
184 | { | ||
185 | if (!resolveUserUuid(part.CreatorID)) | ||
186 | part.CreatorID = masterAvatarId; | ||
187 | |||
188 | if (!resolveUserUuid(part.OwnerID)) | ||
189 | part.OwnerID = masterAvatarId; | ||
190 | |||
191 | if (!resolveUserUuid(part.LastOwnerID)) | ||
192 | part.LastOwnerID = masterAvatarId; | ||
193 | |||
194 | // And zap any troublesome sit target information | ||
195 | part.SitTargetOrientation = new Quaternion(0, 0, 0, 1); | ||
196 | part.SitTargetPosition = new Vector3(0, 0, 0); | ||
197 | |||
198 | // Fix ownership/creator of inventory items | ||
199 | // Not doing so results in inventory items | ||
200 | // being no copy/no mod for everyone | ||
201 | TaskInventoryDictionary inv = part.TaskInventory; | ||
202 | foreach (KeyValuePair<UUID, TaskInventoryItem> kvp in inv) | ||
203 | { | ||
204 | if (!resolveUserUuid(kvp.Value.OwnerID)) | ||
205 | { | ||
206 | kvp.Value.OwnerID = masterAvatarId; | ||
207 | } | ||
208 | if (!resolveUserUuid(kvp.Value.CreatorID)) | ||
209 | { | ||
210 | kvp.Value.CreatorID = masterAvatarId; | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | if (m_scene.AddRestoredSceneObject(sceneObject, true, false)) | ||
216 | { | ||
217 | sceneObjects.Add(sceneObject); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | m_log.InfoFormat("[ARCHIVER]: Restored {0} scene objects to the scene", sceneObjects.Count); | ||
222 | |||
223 | int ignoredObjects = serialisedSceneObjects.Count - sceneObjects.Count; | ||
224 | |||
225 | if (ignoredObjects > 0) | ||
226 | m_log.WarnFormat("[ARCHIVER]: Ignored {0} scene objects that already existed in the scene", ignoredObjects); | ||
227 | |||
228 | m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive"); | ||
229 | |||
230 | m_log.Debug("[ARCHIVER]: Starting scripts"); | ||
231 | |||
232 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
233 | { | ||
234 | sceneObject.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 0); | ||
235 | } | ||
236 | |||
237 | m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage); | ||
238 | } | ||
239 | |||
240 | /// <summary> | ||
241 | /// Look up the given user id to check whether it's one that is valid for this grid. | ||
242 | /// </summary> | ||
243 | /// <param name="uuid"></param> | ||
244 | /// <returns></returns> | ||
245 | private bool resolveUserUuid(UUID uuid) | ||
246 | { | ||
247 | if (!m_validUserUuids.ContainsKey(uuid)) | ||
248 | { | ||
249 | CachedUserInfo profile = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(uuid); | ||
250 | if (profile != null && profile.UserProfile != null) | ||
251 | m_validUserUuids.Add(uuid, true); | ||
252 | else | ||
253 | m_validUserUuids.Add(uuid, false); | ||
254 | } | ||
255 | |||
256 | if (m_validUserUuids[uuid]) | ||
257 | return true; | ||
258 | else | ||
259 | return false; | ||
260 | } | ||
261 | |||
262 | /// <summary> | ||
263 | /// Load an asset | ||
264 | /// </summary> | ||
265 | /// <param name="assetFilename"></param> | ||
266 | /// <param name="data"></param> | ||
267 | /// <returns>true if asset was successfully loaded, false otherwise</returns> | ||
268 | private bool LoadAsset(string assetPath, byte[] data) | ||
269 | { | ||
270 | // Right now we're nastily obtaining the UUID from the filename | ||
271 | string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length); | ||
272 | int i = filename.LastIndexOf(ArchiveConstants.ASSET_EXTENSION_SEPARATOR); | ||
273 | |||
274 | if (i == -1) | ||
275 | { | ||
276 | m_log.ErrorFormat( | ||
277 | "[ARCHIVER]: Could not find extension information in asset path {0} since it's missing the separator {1}. Skipping", | ||
278 | assetPath, ArchiveConstants.ASSET_EXTENSION_SEPARATOR); | ||
279 | |||
280 | return false; | ||
281 | } | ||
282 | |||
283 | string extension = filename.Substring(i); | ||
284 | string uuid = filename.Remove(filename.Length - extension.Length); | ||
285 | |||
286 | if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension)) | ||
287 | { | ||
288 | sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension]; | ||
289 | |||
290 | //m_log.DebugFormat("[ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType); | ||
291 | |||
292 | AssetBase asset = new AssetBase(new UUID(uuid), String.Empty); | ||
293 | asset.Metadata.Type = assetType; | ||
294 | asset.Data = data; | ||
295 | |||
296 | m_scene.AssetCache.AddAsset(asset); | ||
297 | |||
298 | /** | ||
299 | * Create layers on decode for image assets. This is likely to significantly increase the time to load archives so | ||
300 | * it might be best done when dearchive takes place on a separate thread | ||
301 | if (asset.Type=AssetType.Texture) | ||
302 | { | ||
303 | IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>(); | ||
304 | if (cacheLayerDecode != null) | ||
305 | cacheLayerDecode.syncdecode(asset.FullID, asset.Data); | ||
306 | } | ||
307 | */ | ||
308 | |||
309 | return true; | ||
310 | } | ||
311 | else | ||
312 | { | ||
313 | m_log.ErrorFormat( | ||
314 | "[ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}", | ||
315 | assetPath, extension); | ||
316 | |||
317 | return false; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | /// <summary> | ||
322 | /// Load region settings data | ||
323 | /// </summary> | ||
324 | /// <param name="settingsPath"></param> | ||
325 | /// <param name="data"></param> | ||
326 | /// <returns> | ||
327 | /// true if settings were loaded successfully, false otherwise | ||
328 | /// </returns> | ||
329 | private bool LoadRegionSettings(string settingsPath, byte[] data) | ||
330 | { | ||
331 | RegionSettings loadedRegionSettings; | ||
332 | |||
333 | try | ||
334 | { | ||
335 | loadedRegionSettings = RegionSettingsSerializer.Deserialize(data); | ||
336 | } | ||
337 | catch (Exception e) | ||
338 | { | ||
339 | m_log.ErrorFormat( | ||
340 | "[ARCHIVER]: Could not parse region settings file {0}. Ignoring. Exception was {1}", | ||
341 | settingsPath, e); | ||
342 | return false; | ||
343 | } | ||
344 | |||
345 | RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings; | ||
346 | |||
347 | currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit; | ||
348 | currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage; | ||
349 | currentRegionSettings.AllowLandJoinDivide = loadedRegionSettings.AllowLandJoinDivide; | ||
350 | currentRegionSettings.AllowLandResell = loadedRegionSettings.AllowLandResell; | ||
351 | currentRegionSettings.BlockFly = loadedRegionSettings.BlockFly; | ||
352 | currentRegionSettings.BlockShowInSearch = loadedRegionSettings.BlockShowInSearch; | ||
353 | currentRegionSettings.BlockTerraform = loadedRegionSettings.BlockTerraform; | ||
354 | currentRegionSettings.DisableCollisions = loadedRegionSettings.DisableCollisions; | ||
355 | currentRegionSettings.DisablePhysics = loadedRegionSettings.DisablePhysics; | ||
356 | currentRegionSettings.DisableScripts = loadedRegionSettings.DisableScripts; | ||
357 | currentRegionSettings.Elevation1NE = loadedRegionSettings.Elevation1NE; | ||
358 | currentRegionSettings.Elevation1NW = loadedRegionSettings.Elevation1NW; | ||
359 | currentRegionSettings.Elevation1SE = loadedRegionSettings.Elevation1SE; | ||
360 | currentRegionSettings.Elevation1SW = loadedRegionSettings.Elevation1SW; | ||
361 | currentRegionSettings.Elevation2NE = loadedRegionSettings.Elevation2NE; | ||
362 | currentRegionSettings.Elevation2NW = loadedRegionSettings.Elevation2NW; | ||
363 | currentRegionSettings.Elevation2SE = loadedRegionSettings.Elevation2SE; | ||
364 | currentRegionSettings.Elevation2SW = loadedRegionSettings.Elevation2SW; | ||
365 | currentRegionSettings.FixedSun = loadedRegionSettings.FixedSun; | ||
366 | currentRegionSettings.ObjectBonus = loadedRegionSettings.ObjectBonus; | ||
367 | currentRegionSettings.RestrictPushing = loadedRegionSettings.RestrictPushing; | ||
368 | currentRegionSettings.TerrainLowerLimit = loadedRegionSettings.TerrainLowerLimit; | ||
369 | currentRegionSettings.TerrainRaiseLimit = loadedRegionSettings.TerrainRaiseLimit; | ||
370 | currentRegionSettings.TerrainTexture1 = loadedRegionSettings.TerrainTexture1; | ||
371 | currentRegionSettings.TerrainTexture2 = loadedRegionSettings.TerrainTexture2; | ||
372 | currentRegionSettings.TerrainTexture3 = loadedRegionSettings.TerrainTexture3; | ||
373 | currentRegionSettings.TerrainTexture4 = loadedRegionSettings.TerrainTexture4; | ||
374 | currentRegionSettings.UseEstateSun = loadedRegionSettings.UseEstateSun; | ||
375 | currentRegionSettings.WaterHeight = loadedRegionSettings.WaterHeight; | ||
376 | |||
377 | IEstateModule estateModule = m_scene.RequestModuleInterface<IEstateModule>(); | ||
378 | estateModule.sendRegionHandshakeToAll(); | ||
379 | |||
380 | return true; | ||
381 | } | ||
382 | |||
383 | /// <summary> | ||
384 | /// Load terrain data | ||
385 | /// </summary> | ||
386 | /// <param name="terrainPath"></param> | ||
387 | /// <param name="data"></param> | ||
388 | /// <returns> | ||
389 | /// true if terrain was resolved successfully, false otherwise. | ||
390 | /// </returns> | ||
391 | private bool LoadTerrain(string terrainPath, byte[] data) | ||
392 | { | ||
393 | ITerrainModule terrainModule = m_scene.RequestModuleInterface<ITerrainModule>(); | ||
394 | |||
395 | MemoryStream ms = new MemoryStream(data); | ||
396 | terrainModule.LoadFromStream(terrainPath, ms); | ||
397 | ms.Close(); | ||
398 | |||
399 | m_log.DebugFormat("[ARCHIVER]: Restored terrain {0}", terrainPath); | ||
400 | |||
401 | return true; | ||
402 | } | ||
403 | |||
404 | /// <summary> | ||
405 | /// Resolve path to a working FileStream | ||
406 | /// </summary> | ||
407 | private Stream GetStream(string path) | ||
408 | { | ||
409 | try | ||
410 | { | ||
411 | if (File.Exists(path)) | ||
412 | { | ||
413 | return new FileStream(path, FileMode.Open); | ||
414 | } | ||
415 | else | ||
416 | { | ||
417 | Uri uri = new Uri(path); // throw exception if not valid URI | ||
418 | if (uri.Scheme == "file") | ||
419 | { | ||
420 | return new FileStream(uri.AbsolutePath, FileMode.Open); | ||
421 | } | ||
422 | else | ||
423 | { | ||
424 | if (uri.Scheme != "http") | ||
425 | throw new Exception(String.Format("Unsupported URI scheme ({0})", path)); | ||
426 | |||
427 | // OK, now we know we have an HTTP URI to work with | ||
428 | |||
429 | return URIFetch(uri); | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | catch (Exception e) | ||
434 | { | ||
435 | throw new Exception(String.Format("Unable to create file input stream for {0}: {1}", path, e)); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | private static Stream URIFetch(Uri uri) | ||
440 | { | ||
441 | HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); | ||
442 | |||
443 | // request.Credentials = credentials; | ||
444 | |||
445 | request.ContentLength = 0; | ||
446 | |||
447 | WebResponse response = request.GetResponse(); | ||
448 | Stream file = response.GetResponseStream(); | ||
449 | |||
450 | if (response.ContentType != "application/x-oar") | ||
451 | throw new Exception(String.Format("{0} does not identify an OAR file", uri.ToString())); | ||
452 | |||
453 | if (response.ContentLength == 0) | ||
454 | throw new Exception(String.Format("{0} returned an empty file", uri.ToString())); | ||
455 | |||
456 | // return new BufferedStream(file, (int) response.ContentLength); | ||
457 | return new BufferedStream(file, 1000000); | ||
458 | } | ||
459 | } | ||
460 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs new file mode 100644 index 0000000..d3c2cd1 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs | |||
@@ -0,0 +1,161 @@ | |||
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 OpenSim 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 System.Xml; | ||
33 | using OpenMetaverse; | ||
34 | using log4net; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
39 | using OpenSim.Region.CoreModules.World.Terrain; | ||
40 | |||
41 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
42 | { | ||
43 | /// <summary> | ||
44 | /// Method called when all the necessary assets for an archive request have been received. | ||
45 | /// </summary> | ||
46 | public delegate void AssetsRequestCallback(IDictionary<UUID, AssetBase> assetsFound, ICollection<UUID> assetsNotFoundUuids); | ||
47 | |||
48 | /// <summary> | ||
49 | /// Execute the write of an archive once we have received all the necessary data | ||
50 | /// </summary> | ||
51 | public class ArchiveWriteRequestExecution | ||
52 | { | ||
53 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
54 | |||
55 | protected ITerrainModule m_terrainModule; | ||
56 | protected IRegionSerialiserModule m_serialiser; | ||
57 | protected List<SceneObjectGroup> m_sceneObjects; | ||
58 | protected Scene m_scene; | ||
59 | protected Stream m_saveStream; | ||
60 | |||
61 | public ArchiveWriteRequestExecution( | ||
62 | List<SceneObjectGroup> sceneObjects, | ||
63 | ITerrainModule terrainModule, | ||
64 | IRegionSerialiserModule serialiser, | ||
65 | Scene scene, | ||
66 | Stream saveStream) | ||
67 | { | ||
68 | m_sceneObjects = sceneObjects; | ||
69 | m_terrainModule = terrainModule; | ||
70 | m_serialiser = serialiser; | ||
71 | m_scene = scene; | ||
72 | m_saveStream = saveStream; | ||
73 | } | ||
74 | |||
75 | protected internal void ReceivedAllAssets( | ||
76 | IDictionary<UUID, AssetBase> assetsFound, ICollection<UUID> assetsNotFoundUuids) | ||
77 | { | ||
78 | foreach (UUID uuid in assetsNotFoundUuids) | ||
79 | { | ||
80 | m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid); | ||
81 | } | ||
82 | |||
83 | m_log.InfoFormat( | ||
84 | "[ARCHIVER]: Received {0} of {1} assets requested", | ||
85 | assetsFound.Count, assetsFound.Count + assetsNotFoundUuids.Count); | ||
86 | |||
87 | m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time."); | ||
88 | |||
89 | TarArchiveWriter archive = new TarArchiveWriter(); | ||
90 | |||
91 | // Write out control file | ||
92 | archive.AddFile(ArchiveConstants.CONTROL_FILE_PATH, Create0p2ControlFile()); | ||
93 | |||
94 | // Write out region settings | ||
95 | string settingsPath | ||
96 | = String.Format("{0}{1}.xml", ArchiveConstants.SETTINGS_PATH, m_scene.RegionInfo.RegionName); | ||
97 | archive.AddFile(settingsPath, RegionSettingsSerializer.Serialize(m_scene.RegionInfo.RegionSettings)); | ||
98 | |||
99 | // Write out terrain | ||
100 | string terrainPath | ||
101 | = String.Format("{0}{1}.r32", ArchiveConstants.TERRAINS_PATH, m_scene.RegionInfo.RegionName); | ||
102 | |||
103 | MemoryStream ms = new MemoryStream(); | ||
104 | m_terrainModule.SaveToStream(terrainPath, ms); | ||
105 | archive.AddFile(terrainPath, ms.ToArray()); | ||
106 | ms.Close(); | ||
107 | |||
108 | // Write out scene object metadata | ||
109 | foreach (SceneObjectGroup sceneObject in m_sceneObjects) | ||
110 | { | ||
111 | //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType()); | ||
112 | |||
113 | Vector3 position = sceneObject.AbsolutePosition; | ||
114 | |||
115 | string serializedObject = m_serialiser.SaveGroupToXml2(sceneObject); | ||
116 | string filename | ||
117 | = string.Format( | ||
118 | "{0}{1}_{2:000}-{3:000}-{4:000}__{5}.xml", | ||
119 | ArchiveConstants.OBJECTS_PATH, sceneObject.Name, | ||
120 | Math.Round(position.X), Math.Round(position.Y), Math.Round(position.Z), | ||
121 | sceneObject.UUID); | ||
122 | |||
123 | archive.AddFile(filename, serializedObject); | ||
124 | } | ||
125 | |||
126 | // Write out assets | ||
127 | AssetsArchiver assetsArchiver = new AssetsArchiver(assetsFound); | ||
128 | assetsArchiver.Archive(archive); | ||
129 | |||
130 | archive.WriteTar(m_saveStream); | ||
131 | |||
132 | m_log.InfoFormat("[ARCHIVER]: Wrote out OpenSimulator archive for {0}", m_scene.RegionInfo.RegionName); | ||
133 | |||
134 | m_scene.EventManager.TriggerOarFileSaved(String.Empty); | ||
135 | } | ||
136 | |||
137 | /// <summary> | ||
138 | /// Create the control file for a 0.2 version archive | ||
139 | /// </summary> | ||
140 | /// <returns></returns> | ||
141 | public static string Create0p2ControlFile() | ||
142 | { | ||
143 | StringWriter sw = new StringWriter(); | ||
144 | XmlTextWriter xtw = new XmlTextWriter(sw); | ||
145 | xtw.Formatting = Formatting.Indented; | ||
146 | xtw.WriteStartDocument(); | ||
147 | xtw.WriteStartElement("archive"); | ||
148 | xtw.WriteAttributeString("major_version", "0"); | ||
149 | xtw.WriteAttributeString("minor_version", "2"); | ||
150 | xtw.WriteEndElement(); | ||
151 | |||
152 | xtw.Flush(); | ||
153 | xtw.Close(); | ||
154 | |||
155 | String s = sw.ToString(); | ||
156 | sw.Close(); | ||
157 | |||
158 | return s; | ||
159 | } | ||
160 | } | ||
161 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs new file mode 100644 index 0000000..ee0ec69 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | |||
@@ -0,0 +1,333 @@ | |||
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 OpenSim 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 OpenSim.Framework; | ||
29 | using OpenSim.Framework.Communications.Cache; | ||
30 | using OpenSim.Region.Framework.Interfaces; | ||
31 | using OpenSim.Region.Framework.Scenes; | ||
32 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
33 | using OpenSim.Region.CoreModules.World.Terrain; | ||
34 | using System; | ||
35 | using System.Collections.Generic; | ||
36 | using System.IO; | ||
37 | using System.IO.Compression; | ||
38 | using System.Reflection; | ||
39 | using System.Text.RegularExpressions; | ||
40 | using System.Threading; | ||
41 | using OpenMetaverse; | ||
42 | using log4net; | ||
43 | using Nini.Config; | ||
44 | |||
45 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
46 | { | ||
47 | /// <summary> | ||
48 | /// Prepare to write out an archive. | ||
49 | /// </summary> | ||
50 | public class ArchiveWriteRequestPreparation | ||
51 | { | ||
52 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | |||
54 | protected Scene m_scene; | ||
55 | protected Stream m_saveStream; | ||
56 | |||
57 | /// <summary> | ||
58 | /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate | ||
59 | /// asset was found by the asset service. | ||
60 | /// </summary> | ||
61 | protected AssetBase m_requestedObjectAsset; | ||
62 | |||
63 | /// <summary> | ||
64 | /// Signal whether we are currently waiting for the asset service to deliver an asset. | ||
65 | /// </summary> | ||
66 | protected bool m_waitingForObjectAsset; | ||
67 | |||
68 | /// <summary> | ||
69 | /// Constructor | ||
70 | /// </summary> | ||
71 | public ArchiveWriteRequestPreparation(Scene scene, string savePath) | ||
72 | { | ||
73 | m_scene = scene; | ||
74 | m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress); | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Constructor. | ||
79 | /// </summary> | ||
80 | /// <param name="scene"></param> | ||
81 | /// <param name="saveStream">The stream to which to save data.</param> | ||
82 | public ArchiveWriteRequestPreparation(Scene scene, Stream saveStream) | ||
83 | { | ||
84 | m_scene = scene; | ||
85 | m_saveStream = saveStream; | ||
86 | } | ||
87 | |||
88 | /// <summary> | ||
89 | /// The callback made when we request the asset for an object from the asset service. | ||
90 | /// </summary> | ||
91 | public void AssetRequestCallback(UUID assetID, AssetBase asset) | ||
92 | { | ||
93 | lock (this) | ||
94 | { | ||
95 | m_requestedObjectAsset = asset; | ||
96 | m_waitingForObjectAsset = false; | ||
97 | Monitor.Pulse(this); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /// <summary> | ||
102 | /// Get an asset synchronously, potentially using an asynchronous callback. If the | ||
103 | /// asynchronous callback is used, we will wait for it to complete. | ||
104 | /// </summary> | ||
105 | /// <param name="uuid"></param> | ||
106 | /// <returns></returns> | ||
107 | protected AssetBase GetAsset(UUID uuid) | ||
108 | { | ||
109 | m_waitingForObjectAsset = true; | ||
110 | m_scene.AssetCache.GetAsset(uuid, AssetRequestCallback, true); | ||
111 | |||
112 | // The asset cache callback can either | ||
113 | // | ||
114 | // 1. Complete on the same thread (if the asset is already in the cache) or | ||
115 | // 2. Come in via a different thread (if we need to go fetch it). | ||
116 | // | ||
117 | // The code below handles both these alternatives. | ||
118 | lock (this) | ||
119 | { | ||
120 | if (m_waitingForObjectAsset) | ||
121 | { | ||
122 | Monitor.Wait(this); | ||
123 | m_waitingForObjectAsset = false; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | return m_requestedObjectAsset; | ||
128 | } | ||
129 | |||
130 | /// <summary> | ||
131 | /// Record the asset uuids embedded within the given script. | ||
132 | /// </summary> | ||
133 | /// <param name="scriptUuid"></param> | ||
134 | /// <param name="assetUuids">Dictionary in which to record the references</param> | ||
135 | protected void GetScriptAssetUuids(UUID scriptUuid, IDictionary<UUID, int> assetUuids) | ||
136 | { | ||
137 | AssetBase scriptAsset = GetAsset(scriptUuid); | ||
138 | |||
139 | if (null != scriptAsset) | ||
140 | { | ||
141 | string script = Utils.BytesToString(scriptAsset.Data); | ||
142 | //m_log.DebugFormat("[ARCHIVER]: Script {0}", script); | ||
143 | MatchCollection uuidMatches = Util.UUIDPattern.Matches(script); | ||
144 | //m_log.DebugFormat("[ARCHIVER]: Found {0} matches in script", uuidMatches.Count); | ||
145 | |||
146 | foreach (Match uuidMatch in uuidMatches) | ||
147 | { | ||
148 | UUID uuid = new UUID(uuidMatch.Value); | ||
149 | //m_log.DebugFormat("[ARCHIVER]: Recording {0} in script", uuid); | ||
150 | assetUuids[uuid] = 1; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /// <summary> | ||
156 | /// Record the uuids referenced by the given wearable asset | ||
157 | /// </summary> | ||
158 | /// <param name="wearableAssetUuid"></param> | ||
159 | /// <param name="assetUuids">Dictionary in which to record the references</param> | ||
160 | protected void GetWearableAssetUuids(UUID wearableAssetUuid, IDictionary<UUID, int> assetUuids) | ||
161 | { | ||
162 | AssetBase assetBase = GetAsset(wearableAssetUuid); | ||
163 | //m_log.Debug(new System.Text.ASCIIEncoding().GetString(bodypartAsset.Data)); | ||
164 | AssetWearable wearableAsset = new AssetBodypart(wearableAssetUuid, assetBase.Data); | ||
165 | wearableAsset.Decode(); | ||
166 | |||
167 | //m_log.DebugFormat( | ||
168 | // "[ARCHIVER]: Wearable asset {0} references {1} assets", wearableAssetUuid, wearableAsset.Textures.Count); | ||
169 | |||
170 | foreach (UUID uuid in wearableAsset.Textures.Values) | ||
171 | { | ||
172 | //m_log.DebugFormat("[ARCHIVER]: Got bodypart uuid {0}", uuid); | ||
173 | assetUuids[uuid] = 1; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /// <summary> | ||
178 | /// Get all the asset uuids associated with a given object. This includes both those directly associated with | ||
179 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
180 | /// within this object). | ||
181 | /// </summary> | ||
182 | /// <param name="sceneObject"></param> | ||
183 | /// <param name="assetUuids"></param> | ||
184 | protected void GetSceneObjectAssetUuids(UUID sceneObjectUuid, IDictionary<UUID, int> assetUuids) | ||
185 | { | ||
186 | AssetBase objectAsset = GetAsset(sceneObjectUuid); | ||
187 | |||
188 | if (null != objectAsset) | ||
189 | { | ||
190 | string xml = Utils.BytesToString(objectAsset.Data); | ||
191 | SceneObjectGroup sog = new SceneObjectGroup(xml, true); | ||
192 | GetSceneObjectAssetUuids(sog, assetUuids); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | /// <summary> | ||
197 | /// Get all the asset uuids associated with a given object. This includes both those directly associated with | ||
198 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
199 | /// within this object). | ||
200 | /// </summary> | ||
201 | /// <param name="sceneObject"></param> | ||
202 | /// <param name="assetUuids"></param> | ||
203 | protected void GetSceneObjectAssetUuids(SceneObjectGroup sceneObject, IDictionary<UUID, int> assetUuids) | ||
204 | { | ||
205 | m_log.DebugFormat( | ||
206 | "[ARCHIVER]: Getting assets for object {0}, {1}", sceneObject.Name, sceneObject.UUID); | ||
207 | |||
208 | foreach (SceneObjectPart part in sceneObject.GetParts()) | ||
209 | { | ||
210 | //m_log.DebugFormat( | ||
211 | // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); | ||
212 | |||
213 | try | ||
214 | { | ||
215 | Primitive.TextureEntry textureEntry = part.Shape.Textures; | ||
216 | |||
217 | // Get the prim's default texture. This will be used for faces which don't have their own texture | ||
218 | assetUuids[textureEntry.DefaultTexture.TextureID] = 1; | ||
219 | |||
220 | // XXX: Not a great way to iterate through face textures, but there's no | ||
221 | // other method available to tell how many faces there actually are | ||
222 | //int i = 0; | ||
223 | foreach (Primitive.TextureEntryFace texture in textureEntry.FaceTextures) | ||
224 | { | ||
225 | if (texture != null) | ||
226 | { | ||
227 | //m_log.DebugFormat("[ARCHIVER]: Got face {0}", i++); | ||
228 | assetUuids[texture.TextureID] = 1; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | // If the prim is a sculpt then preserve this information too | ||
233 | if (part.Shape.SculptTexture != UUID.Zero) | ||
234 | assetUuids[part.Shape.SculptTexture] = 1; | ||
235 | |||
236 | // Now analyze this prim's inventory items to preserve all the uuids that they reference | ||
237 | foreach (TaskInventoryItem tii in part.TaskInventory.Values) | ||
238 | { | ||
239 | //m_log.DebugFormat("[ARCHIVER]: Analysing item asset type {0}", tii.Type); | ||
240 | |||
241 | if (!assetUuids.ContainsKey(tii.AssetID)) | ||
242 | { | ||
243 | assetUuids[tii.AssetID] = 1; | ||
244 | |||
245 | if ((int)AssetType.Bodypart == tii.Type || ((int)AssetType.Clothing == tii.Type)) | ||
246 | { | ||
247 | GetWearableAssetUuids(tii.AssetID, assetUuids); | ||
248 | } | ||
249 | else if ((int)AssetType.LSLText == tii.Type) | ||
250 | { | ||
251 | GetScriptAssetUuids(tii.AssetID, assetUuids); | ||
252 | } | ||
253 | else if ((int)AssetType.Object == tii.Type) | ||
254 | { | ||
255 | GetSceneObjectAssetUuids(tii.AssetID, assetUuids); | ||
256 | } | ||
257 | //else | ||
258 | //{ | ||
259 | //m_log.DebugFormat("[ARCHIVER]: Recording asset {0} in object {1}", tii.AssetID, part.UUID); | ||
260 | //} | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | catch (Exception e) | ||
265 | { | ||
266 | m_log.ErrorFormat("[ARCHIVER]: Failed to get part - {0}", e); | ||
267 | m_log.DebugFormat("[ARCHIVER]: Texture entry length for prim was {0} (min is 46)", part.Shape.TextureEntry.Length); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /// <summary> | ||
273 | /// Archive the region requested. | ||
274 | /// </summary> | ||
275 | /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception> | ||
276 | public void ArchiveRegion() | ||
277 | { | ||
278 | Dictionary<UUID, int> assetUuids = new Dictionary<UUID, int>(); | ||
279 | |||
280 | List<EntityBase> entities = m_scene.GetEntities(); | ||
281 | List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | ||
282 | |||
283 | // Filter entities so that we only have scene objects. | ||
284 | // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods | ||
285 | // end up having to do this | ||
286 | foreach (EntityBase entity in entities) | ||
287 | { | ||
288 | if (entity is SceneObjectGroup) | ||
289 | { | ||
290 | SceneObjectGroup sceneObject = (SceneObjectGroup)entity; | ||
291 | |||
292 | if (!sceneObject.IsDeleted && !sceneObject.IsAttachment) | ||
293 | sceneObjects.Add((SceneObjectGroup)entity); | ||
294 | } | ||
295 | } | ||
296 | |||
297 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
298 | { | ||
299 | GetSceneObjectAssetUuids(sceneObject, assetUuids); | ||
300 | } | ||
301 | |||
302 | m_log.DebugFormat( | ||
303 | "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets", | ||
304 | sceneObjects.Count, assetUuids.Count); | ||
305 | |||
306 | // Make sure that we also request terrain texture assets | ||
307 | RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings; | ||
308 | |||
309 | if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1) | ||
310 | assetUuids[regionSettings.TerrainTexture1] = 1; | ||
311 | |||
312 | if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2) | ||
313 | assetUuids[regionSettings.TerrainTexture2] = 1; | ||
314 | |||
315 | if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3) | ||
316 | assetUuids[regionSettings.TerrainTexture3] = 1; | ||
317 | |||
318 | if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4) | ||
319 | assetUuids[regionSettings.TerrainTexture4] = 1; | ||
320 | |||
321 | // Asynchronously request all the assets required to perform this archive operation | ||
322 | ArchiveWriteRequestExecution awre | ||
323 | = new ArchiveWriteRequestExecution( | ||
324 | sceneObjects, | ||
325 | m_scene.RequestModuleInterface<ITerrainModule>(), | ||
326 | m_scene.RequestModuleInterface<IRegionSerialiserModule>(), | ||
327 | m_scene, | ||
328 | m_saveStream); | ||
329 | |||
330 | new AssetsRequest(assetUuids.Keys, m_scene.AssetCache, awre.ReceivedAllAssets).Execute(); | ||
331 | } | ||
332 | } | ||
333 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs new file mode 100644 index 0000000..c1f5b18 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs | |||
@@ -0,0 +1,95 @@ | |||
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 OpenSim 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.Collections.Generic; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Threading; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using Nini.Config; | ||
35 | using OpenSim.Framework.Communications.Cache; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
39 | |||
40 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
41 | { | ||
42 | /// <summary> | ||
43 | /// This module loads and saves OpenSimulator archives | ||
44 | /// </summary> | ||
45 | public class ArchiverModule : IRegionModule, IRegionArchiverModule | ||
46 | { | ||
47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
48 | |||
49 | private Scene m_scene; | ||
50 | |||
51 | public string Name { get { return "Archiver Module"; } } | ||
52 | |||
53 | public bool IsSharedModule { get { return false; } } | ||
54 | |||
55 | public void Initialise(Scene scene, IConfigSource source) | ||
56 | { | ||
57 | m_scene = scene; | ||
58 | m_scene.RegisterModuleInterface<IRegionArchiverModule>(this); | ||
59 | } | ||
60 | |||
61 | public void PostInitialise() | ||
62 | { | ||
63 | } | ||
64 | |||
65 | public void Close() | ||
66 | { | ||
67 | } | ||
68 | |||
69 | public void ArchiveRegion(string savePath) | ||
70 | { | ||
71 | m_log.InfoFormat( | ||
72 | "[ARCHIVER]: Writing archive for region {0} to {1}", m_scene.RegionInfo.RegionName, savePath); | ||
73 | |||
74 | new ArchiveWriteRequestPreparation(m_scene, savePath).ArchiveRegion(); | ||
75 | } | ||
76 | |||
77 | public void ArchiveRegion(Stream saveStream) | ||
78 | { | ||
79 | new ArchiveWriteRequestPreparation(m_scene, saveStream).ArchiveRegion(); | ||
80 | } | ||
81 | |||
82 | public void DearchiveRegion(string loadPath) | ||
83 | { | ||
84 | m_log.InfoFormat( | ||
85 | "[ARCHIVER]: Loading archive to region {0} from {1}", m_scene.RegionInfo.RegionName, loadPath); | ||
86 | |||
87 | new ArchiveReadRequest(m_scene, loadPath).DearchiveRegion(); | ||
88 | } | ||
89 | |||
90 | public void DearchiveRegion(Stream loadStream) | ||
91 | { | ||
92 | new ArchiveReadRequest(m_scene, loadStream).DearchiveRegion(); | ||
93 | } | ||
94 | } | ||
95 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs new file mode 100644 index 0000000..76d27ce --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs | |||
@@ -0,0 +1,143 @@ | |||
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 OpenSim 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.Collections.Generic; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Xml; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using OpenSim.Framework; | ||
35 | |||
36 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// Archives assets | ||
40 | /// </summary> | ||
41 | public class AssetsArchiver | ||
42 | { | ||
43 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
44 | |||
45 | /// <summary> | ||
46 | /// Archive assets | ||
47 | /// </summary> | ||
48 | protected IDictionary<UUID, AssetBase> m_assets; | ||
49 | |||
50 | public AssetsArchiver(IDictionary<UUID, AssetBase> assets) | ||
51 | { | ||
52 | m_assets = assets; | ||
53 | } | ||
54 | |||
55 | /// <summary> | ||
56 | /// Archive the assets given to this archiver to the given archive. | ||
57 | /// </summary> | ||
58 | /// <param name="archive"></param> | ||
59 | public void Archive(TarArchiveWriter archive) | ||
60 | { | ||
61 | //WriteMetadata(archive); | ||
62 | WriteData(archive); | ||
63 | } | ||
64 | |||
65 | /// <summary> | ||
66 | /// Write an assets metadata file to the given archive | ||
67 | /// </summary> | ||
68 | /// <param name="archive"></param> | ||
69 | protected void WriteMetadata(TarArchiveWriter archive) | ||
70 | { | ||
71 | StringWriter sw = new StringWriter(); | ||
72 | XmlTextWriter xtw = new XmlTextWriter(sw); | ||
73 | |||
74 | xtw.Formatting = Formatting.Indented; | ||
75 | xtw.WriteStartDocument(); | ||
76 | |||
77 | xtw.WriteStartElement("assets"); | ||
78 | |||
79 | foreach (UUID uuid in m_assets.Keys) | ||
80 | { | ||
81 | AssetBase asset = m_assets[uuid]; | ||
82 | |||
83 | if (asset != null) | ||
84 | { | ||
85 | xtw.WriteStartElement("asset"); | ||
86 | |||
87 | string extension = string.Empty; | ||
88 | |||
89 | if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.Metadata.Type)) | ||
90 | { | ||
91 | extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.Metadata.Type]; | ||
92 | } | ||
93 | |||
94 | xtw.WriteElementString("filename", uuid.ToString() + extension); | ||
95 | |||
96 | xtw.WriteElementString("name", asset.Metadata.Name); | ||
97 | xtw.WriteElementString("description", asset.Metadata.Description); | ||
98 | xtw.WriteElementString("asset-type", asset.Metadata.Type.ToString()); | ||
99 | |||
100 | xtw.WriteEndElement(); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | xtw.WriteEndElement(); | ||
105 | |||
106 | xtw.WriteEndDocument(); | ||
107 | |||
108 | archive.AddFile("assets.xml", sw.ToString()); | ||
109 | } | ||
110 | |||
111 | /// <summary> | ||
112 | /// Write asset data files to the given archive | ||
113 | /// </summary> | ||
114 | /// <param name="archive"></param> | ||
115 | protected void WriteData(TarArchiveWriter archive) | ||
116 | { | ||
117 | // It appears that gtar, at least, doesn't need the intermediate directory entries in the tar | ||
118 | //archive.AddDir("assets"); | ||
119 | |||
120 | foreach (UUID uuid in m_assets.Keys) | ||
121 | { | ||
122 | AssetBase asset = m_assets[uuid]; | ||
123 | |||
124 | string extension = string.Empty; | ||
125 | |||
126 | if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.Metadata.Type)) | ||
127 | { | ||
128 | extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.Metadata.Type]; | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | m_log.ErrorFormat( | ||
133 | "[ARCHIVER]: Unrecognized asset type {0} with uuid {1}. This asset will be saved but not reloaded", | ||
134 | asset.Metadata.Type, asset.Metadata.ID); | ||
135 | } | ||
136 | |||
137 | archive.AddFile( | ||
138 | ArchiveConstants.ASSETS_PATH + uuid.ToString() + extension, | ||
139 | asset.Data); | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs new file mode 100644 index 0000000..f9909d9 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs | |||
@@ -0,0 +1,184 @@ | |||
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 OpenSim 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 System.Xml; | ||
33 | using OpenMetaverse; | ||
34 | using log4net; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Communications.Cache; | ||
37 | |||
38 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
39 | { | ||
40 | /// <summary> | ||
41 | /// Dearchives assets | ||
42 | /// </summary> | ||
43 | public class AssetsDearchiver | ||
44 | { | ||
45 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
46 | |||
47 | protected static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding(); | ||
48 | |||
49 | /// <summary> | ||
50 | /// Store for asset data we received before we get the metadata | ||
51 | /// </summary> | ||
52 | protected Dictionary<string, byte[]> m_assetDataAwaitingMetadata = new Dictionary<string, byte[]>(); | ||
53 | |||
54 | /// <summary> | ||
55 | /// Asset metadata. Is null if asset metadata isn't yet available. | ||
56 | /// </summary> | ||
57 | protected Dictionary<string, AssetMetadata> m_metadata; | ||
58 | |||
59 | /// <summary> | ||
60 | /// Cache to which dearchived assets will be added | ||
61 | /// </summary> | ||
62 | protected IAssetCache m_cache; | ||
63 | |||
64 | public AssetsDearchiver(IAssetCache cache) | ||
65 | { | ||
66 | m_cache = cache; | ||
67 | } | ||
68 | |||
69 | /// <summary> | ||
70 | /// Add asset data to the dearchiver | ||
71 | /// </summary> | ||
72 | /// <param name="assetFilename"></param> | ||
73 | /// <param name="data"></param> | ||
74 | public void AddAssetData(string assetFilename, byte[] data) | ||
75 | { | ||
76 | if (null == m_metadata) | ||
77 | { | ||
78 | m_assetDataAwaitingMetadata[assetFilename] = data; | ||
79 | } | ||
80 | else | ||
81 | { | ||
82 | ResolveAssetData(assetFilename, data); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /// <summary> | ||
87 | /// Add asset metadata xml | ||
88 | /// </summary> | ||
89 | /// <param name="xml"></param> | ||
90 | public void AddAssetMetadata(string xml) | ||
91 | { | ||
92 | m_metadata = new Dictionary<string, AssetMetadata>(); | ||
93 | |||
94 | StringReader sr = new StringReader(xml); | ||
95 | XmlTextReader reader = new XmlTextReader(sr); | ||
96 | |||
97 | reader.ReadStartElement("assets"); | ||
98 | reader.Read(); | ||
99 | |||
100 | while (reader.Name.Equals("asset")) | ||
101 | { | ||
102 | reader.Read(); | ||
103 | |||
104 | AssetMetadata metadata = new AssetMetadata(); | ||
105 | |||
106 | string filename = reader.ReadElementString("filename"); | ||
107 | m_log.DebugFormat("[DEARCHIVER]: Reading node {0}", filename); | ||
108 | |||
109 | metadata.Name = reader.ReadElementString("name"); | ||
110 | metadata.Description = reader.ReadElementString("description"); | ||
111 | metadata.AssetType = Convert.ToSByte(reader.ReadElementString("asset-type")); | ||
112 | |||
113 | m_metadata[filename] = metadata; | ||
114 | |||
115 | // Read asset end tag | ||
116 | reader.ReadEndElement(); | ||
117 | |||
118 | reader.Read(); | ||
119 | } | ||
120 | |||
121 | m_log.DebugFormat("[DEARCHIVER]: Resolved {0} items of asset metadata", m_metadata.Count); | ||
122 | |||
123 | ResolvePendingAssetData(); | ||
124 | } | ||
125 | |||
126 | /// <summary> | ||
127 | /// Resolve asset data that we collected before receiving the metadata | ||
128 | /// </summary> | ||
129 | protected void ResolvePendingAssetData() | ||
130 | { | ||
131 | foreach (string filename in m_assetDataAwaitingMetadata.Keys) | ||
132 | { | ||
133 | ResolveAssetData(filename, m_assetDataAwaitingMetadata[filename]); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | /// <summary> | ||
138 | /// Resolve a new piece of asset data against stored metadata | ||
139 | /// </summary> | ||
140 | /// <param name="assetFilename"></param> | ||
141 | /// <param name="data"></param> | ||
142 | protected void ResolveAssetData(string assetPath, byte[] data) | ||
143 | { | ||
144 | // Right now we're nastily obtaining the UUID from the filename | ||
145 | string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length); | ||
146 | |||
147 | if (m_metadata.ContainsKey(filename)) | ||
148 | { | ||
149 | AssetMetadata metadata = m_metadata[filename]; | ||
150 | |||
151 | if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(metadata.AssetType)) | ||
152 | { | ||
153 | string extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[metadata.AssetType]; | ||
154 | filename = filename.Remove(filename.Length - extension.Length); | ||
155 | } | ||
156 | |||
157 | m_log.DebugFormat("[ARCHIVER]: Importing asset {0}", filename); | ||
158 | |||
159 | AssetBase asset = new AssetBase(new UUID(filename), metadata.Name); | ||
160 | asset.Metadata.Description = metadata.Description; | ||
161 | asset.Metadata.Type = metadata.AssetType; | ||
162 | asset.Data = data; | ||
163 | |||
164 | m_cache.AddAsset(asset); | ||
165 | } | ||
166 | else | ||
167 | { | ||
168 | m_log.ErrorFormat( | ||
169 | "[DEARCHIVER]: Tried to dearchive data with filename {0} without any corresponding metadata", | ||
170 | assetPath); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /// <summary> | ||
175 | /// Metadata for an asset | ||
176 | /// </summary> | ||
177 | protected struct AssetMetadata | ||
178 | { | ||
179 | public string Name; | ||
180 | public string Description; | ||
181 | public sbyte AssetType; | ||
182 | } | ||
183 | } | ||
184 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs new file mode 100644 index 0000000..8971b6e --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs | |||
@@ -0,0 +1,138 @@ | |||
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 OpenSim 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.Reflection; | ||
31 | using System.Threading; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Framework.Communications.Cache; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | |||
39 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
40 | { | ||
41 | /// <summary> | ||
42 | /// Encapsulate the asynchronous requests for the assets required for an archive operation | ||
43 | /// </summary> | ||
44 | class AssetsRequest | ||
45 | { | ||
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | |||
48 | /// <summary> | ||
49 | /// uuids to request | ||
50 | /// </summary> | ||
51 | protected ICollection<UUID> m_uuids; | ||
52 | |||
53 | /// <summary> | ||
54 | /// Callback used when all the assets requested have been received. | ||
55 | /// </summary> | ||
56 | protected AssetsRequestCallback m_assetsRequestCallback; | ||
57 | |||
58 | /// <summary> | ||
59 | /// Assets retrieved in this request | ||
60 | /// </summary> | ||
61 | protected Dictionary<UUID, AssetBase> m_assets = new Dictionary<UUID, AssetBase>(); | ||
62 | |||
63 | /// <summary> | ||
64 | /// Maintain a list of assets that could not be found. This will be passed back to the requester. | ||
65 | /// </summary> | ||
66 | protected List<UUID> m_notFoundAssetUuids = new List<UUID>(); | ||
67 | |||
68 | /// <summary> | ||
69 | /// Record the number of asset replies required so we know when we've finished | ||
70 | /// </summary> | ||
71 | private int m_repliesRequired; | ||
72 | |||
73 | /// <summary> | ||
74 | /// Asset cache used to request the assets | ||
75 | /// </summary> | ||
76 | protected IAssetCache m_assetCache; | ||
77 | |||
78 | protected internal AssetsRequest(ICollection<UUID> uuids, IAssetCache assetCache, AssetsRequestCallback assetsRequestCallback) | ||
79 | { | ||
80 | m_uuids = uuids; | ||
81 | m_assetsRequestCallback = assetsRequestCallback; | ||
82 | m_assetCache = assetCache; | ||
83 | m_repliesRequired = uuids.Count; | ||
84 | } | ||
85 | |||
86 | protected internal void Execute() | ||
87 | { | ||
88 | // We can stop here if there are no assets to fetch | ||
89 | if (m_repliesRequired == 0) | ||
90 | m_assetsRequestCallback(m_assets, m_notFoundAssetUuids); | ||
91 | |||
92 | foreach (UUID uuid in m_uuids) | ||
93 | { | ||
94 | m_assetCache.GetAsset(uuid, AssetRequestCallback, true); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /// <summary> | ||
99 | /// Called back by the asset cache when it has the asset | ||
100 | /// </summary> | ||
101 | /// <param name="assetID"></param> | ||
102 | /// <param name="asset"></param> | ||
103 | public void AssetRequestCallback(UUID assetID, AssetBase asset) | ||
104 | { | ||
105 | if (asset != null) | ||
106 | m_assets[assetID] = asset; | ||
107 | else | ||
108 | m_notFoundAssetUuids.Add(assetID); | ||
109 | |||
110 | //m_log.DebugFormat( | ||
111 | // "[ARCHIVER]: Received {0} assets and notification of {1} missing assets", m_assets.Count, m_notFoundAssetUuids.Count); | ||
112 | |||
113 | if (m_assets.Count + m_notFoundAssetUuids.Count == m_repliesRequired) | ||
114 | { | ||
115 | // We want to stop using the asset cache thread asap as we now need to do the actual work of producing the archive | ||
116 | Thread newThread = new Thread(PerformAssetsRequestCallback); | ||
117 | newThread.Name = "OpenSimulator archiving thread post assets receipt"; | ||
118 | newThread.Start(); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | /// <summary> | ||
123 | /// Perform the callback on the original requester of the assets | ||
124 | /// </summary> | ||
125 | protected void PerformAssetsRequestCallback() | ||
126 | { | ||
127 | try | ||
128 | { | ||
129 | m_assetsRequestCallback(m_assets, m_notFoundAssetUuids); | ||
130 | } | ||
131 | catch (Exception e) | ||
132 | { | ||
133 | m_log.ErrorFormat( | ||
134 | "[ARCHIVER]: Terminating archive creation since asset requster callback failed with {0}", e); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs b/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs new file mode 100644 index 0000000..2580316 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs | |||
@@ -0,0 +1,258 @@ | |||
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 OpenSim 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.IO; | ||
30 | using System.Text; | ||
31 | using System.Xml; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
36 | { | ||
37 | /// <summary> | ||
38 | /// Serialize and deserialize region settings for an archive file format. | ||
39 | /// </summary> | ||
40 | /// We didn't simply use automatic .NET serializagion for OpenSim.Framework.RegionSettings since this is really | ||
41 | /// a file format rather than an object serialization. | ||
42 | /// TODO: However, we could still have used separate non-framework classes here to read and write the xml | ||
43 | /// automatically rather than laboriously doing it by hand using XmlTextReader and Writer. Should switch to this | ||
44 | /// in the future. | ||
45 | public class RegionSettingsSerializer | ||
46 | { | ||
47 | protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding(); | ||
48 | |||
49 | /// <summary> | ||
50 | /// Deserialize region settings | ||
51 | /// </summary> | ||
52 | /// <param name="serializedSettings"></param> | ||
53 | /// <returns></returns> | ||
54 | /// <exception cref="System.Xml.XmlException"></exception> | ||
55 | public static RegionSettings Deserialize(byte[] serializedSettings) | ||
56 | { | ||
57 | return Deserialize(m_asciiEncoding.GetString(serializedSettings, 0, serializedSettings.Length)); | ||
58 | } | ||
59 | |||
60 | /// <summary> | ||
61 | /// Deserialize region settings | ||
62 | /// </summary> | ||
63 | /// <param name="serializedSettings"></param> | ||
64 | /// <returns></returns> | ||
65 | /// <exception cref="System.Xml.XmlException"></exception> | ||
66 | public static RegionSettings Deserialize(string serializedSettings) | ||
67 | { | ||
68 | RegionSettings settings = new RegionSettings(); | ||
69 | |||
70 | StringReader sr = new StringReader(serializedSettings); | ||
71 | XmlTextReader xtr = new XmlTextReader(sr); | ||
72 | |||
73 | xtr.ReadStartElement("RegionSettings"); | ||
74 | |||
75 | xtr.ReadStartElement("General"); | ||
76 | |||
77 | while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) | ||
78 | { | ||
79 | switch (xtr.Name) | ||
80 | { | ||
81 | case "AllowDamage": | ||
82 | settings.AllowDamage = bool.Parse(xtr.ReadElementContentAsString()); | ||
83 | break; | ||
84 | case "AllowLandResell": | ||
85 | settings.AllowLandResell = bool.Parse(xtr.ReadElementContentAsString()); | ||
86 | break; | ||
87 | case "AllowLandJoinDivide": | ||
88 | settings.AllowLandJoinDivide = bool.Parse(xtr.ReadElementContentAsString()); | ||
89 | break; | ||
90 | case "BlockFly": | ||
91 | settings.BlockFly = bool.Parse(xtr.ReadElementContentAsString()); | ||
92 | break; | ||
93 | case "BlockLandShowInSearch": | ||
94 | settings.BlockShowInSearch = bool.Parse(xtr.ReadElementContentAsString()); | ||
95 | break; | ||
96 | case "BlockTerraform": | ||
97 | settings.BlockTerraform = bool.Parse(xtr.ReadElementContentAsString()); | ||
98 | break; | ||
99 | case "DisableCollisions": | ||
100 | settings.DisableCollisions = bool.Parse(xtr.ReadElementContentAsString()); | ||
101 | break; | ||
102 | case "DisablePhysics": | ||
103 | settings.DisablePhysics = bool.Parse(xtr.ReadElementContentAsString()); | ||
104 | break; | ||
105 | case "DisableScripts": | ||
106 | settings.DisableScripts = bool.Parse(xtr.ReadElementContentAsString()); | ||
107 | break; | ||
108 | case "MaturityRating": | ||
109 | settings.Maturity = int.Parse(xtr.ReadElementContentAsString()); | ||
110 | break; | ||
111 | case "RestrictPushing": | ||
112 | settings.RestrictPushing = bool.Parse(xtr.ReadElementContentAsString()); | ||
113 | break; | ||
114 | case "AgentLimit": | ||
115 | settings.AgentLimit = int.Parse(xtr.ReadElementContentAsString()); | ||
116 | break; | ||
117 | case "ObjectBonus": | ||
118 | settings.ObjectBonus = double.Parse(xtr.ReadElementContentAsString()); | ||
119 | break; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | xtr.ReadEndElement(); | ||
124 | xtr.ReadStartElement("GroundTextures"); | ||
125 | |||
126 | while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) | ||
127 | { | ||
128 | switch (xtr.Name) | ||
129 | { | ||
130 | case "Texture1": | ||
131 | settings.TerrainTexture1 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
132 | break; | ||
133 | case "Texture2": | ||
134 | settings.TerrainTexture2 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
135 | break; | ||
136 | case "Texture3": | ||
137 | settings.TerrainTexture3 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
138 | break; | ||
139 | case "Texture4": | ||
140 | settings.TerrainTexture4 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
141 | break; | ||
142 | case "ElevationLowSW": | ||
143 | settings.Elevation1SW = double.Parse(xtr.ReadElementContentAsString()); | ||
144 | break; | ||
145 | case "ElevationLowNW": | ||
146 | settings.Elevation1NW = double.Parse(xtr.ReadElementContentAsString()); | ||
147 | break; | ||
148 | case "ElevationLowSE": | ||
149 | settings.Elevation1SE = double.Parse(xtr.ReadElementContentAsString()); | ||
150 | break; | ||
151 | case "ElevationLowNE": | ||
152 | settings.Elevation1NE = double.Parse(xtr.ReadElementContentAsString()); | ||
153 | break; | ||
154 | case "ElevationHighSW": | ||
155 | settings.Elevation1SW = double.Parse(xtr.ReadElementContentAsString()); | ||
156 | break; | ||
157 | case "ElevationHighNW": | ||
158 | settings.Elevation2NW = double.Parse(xtr.ReadElementContentAsString()); | ||
159 | break; | ||
160 | case "ElevationHighSE": | ||
161 | settings.Elevation2SE = double.Parse(xtr.ReadElementContentAsString()); | ||
162 | break; | ||
163 | case "ElevationHighNE": | ||
164 | settings.Elevation2NE = double.Parse(xtr.ReadElementContentAsString()); | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | xtr.ReadEndElement(); | ||
170 | xtr.ReadStartElement("Terrain"); | ||
171 | |||
172 | while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) | ||
173 | { | ||
174 | switch (xtr.Name) | ||
175 | { | ||
176 | case "WaterHeight": | ||
177 | settings.WaterHeight = double.Parse(xtr.ReadElementContentAsString()); | ||
178 | break; | ||
179 | case "TerrainRaiseLimit": | ||
180 | settings.TerrainRaiseLimit = double.Parse(xtr.ReadElementContentAsString()); | ||
181 | break; | ||
182 | case "TerrainLowerLimit": | ||
183 | settings.TerrainLowerLimit = double.Parse(xtr.ReadElementContentAsString()); | ||
184 | break; | ||
185 | case "UseEstateSun": | ||
186 | settings.UseEstateSun = bool.Parse(xtr.ReadElementContentAsString()); | ||
187 | break; | ||
188 | case "FixedSun": | ||
189 | settings.FixedSun = bool.Parse(xtr.ReadElementContentAsString()); | ||
190 | break; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | xtr.Close(); | ||
195 | sr.Close(); | ||
196 | |||
197 | return settings; | ||
198 | } | ||
199 | |||
200 | public static string Serialize(RegionSettings settings) | ||
201 | { | ||
202 | StringWriter sw = new StringWriter(); | ||
203 | XmlTextWriter xtw = new XmlTextWriter(sw); | ||
204 | xtw.Formatting = Formatting.Indented; | ||
205 | xtw.WriteStartDocument(); | ||
206 | |||
207 | xtw.WriteStartElement("RegionSettings"); | ||
208 | |||
209 | xtw.WriteStartElement("General"); | ||
210 | xtw.WriteElementString("AllowDamage", settings.AllowDamage.ToString()); | ||
211 | xtw.WriteElementString("AllowLandResell", settings.AllowLandResell.ToString()); | ||
212 | xtw.WriteElementString("AllowLandJoinDivide", settings.AllowLandJoinDivide.ToString()); | ||
213 | xtw.WriteElementString("BlockFly", settings.BlockFly.ToString()); | ||
214 | xtw.WriteElementString("BlockLandShowInSearch", settings.BlockShowInSearch.ToString()); | ||
215 | xtw.WriteElementString("BlockTerraform", settings.BlockTerraform.ToString()); | ||
216 | xtw.WriteElementString("DisableCollisions", settings.DisableCollisions.ToString()); | ||
217 | xtw.WriteElementString("DisablePhysics", settings.DisablePhysics.ToString()); | ||
218 | xtw.WriteElementString("DisableScripts", settings.DisableScripts.ToString()); | ||
219 | xtw.WriteElementString("MaturityRating", settings.Maturity.ToString()); | ||
220 | xtw.WriteElementString("RestrictPushing", settings.RestrictPushing.ToString()); | ||
221 | xtw.WriteElementString("AgentLimit", settings.AgentLimit.ToString()); | ||
222 | xtw.WriteElementString("ObjectBonus", settings.ObjectBonus.ToString()); | ||
223 | xtw.WriteEndElement(); | ||
224 | |||
225 | xtw.WriteStartElement("GroundTextures"); | ||
226 | xtw.WriteElementString("Texture1", settings.TerrainTexture1.ToString()); | ||
227 | xtw.WriteElementString("Texture2", settings.TerrainTexture2.ToString()); | ||
228 | xtw.WriteElementString("Texture3", settings.TerrainTexture3.ToString()); | ||
229 | xtw.WriteElementString("Texture4", settings.TerrainTexture4.ToString()); | ||
230 | xtw.WriteElementString("ElevationLowSW", settings.Elevation1SW.ToString()); | ||
231 | xtw.WriteElementString("ElevationLowNW", settings.Elevation1NW.ToString()); | ||
232 | xtw.WriteElementString("ElevationLowSE", settings.Elevation1SE.ToString()); | ||
233 | xtw.WriteElementString("ElevationLowNE", settings.Elevation1NE.ToString()); | ||
234 | xtw.WriteElementString("ElevationHighSW", settings.Elevation2SW.ToString()); | ||
235 | xtw.WriteElementString("ElevationHighNW", settings.Elevation2NW.ToString()); | ||
236 | xtw.WriteElementString("ElevationHighSE", settings.Elevation2SE.ToString()); | ||
237 | xtw.WriteElementString("ElevationHighNE", settings.Elevation2NE.ToString()); | ||
238 | xtw.WriteEndElement(); | ||
239 | |||
240 | xtw.WriteStartElement("Terrain"); | ||
241 | xtw.WriteElementString("WaterHeight", settings.WaterHeight.ToString()); | ||
242 | xtw.WriteElementString("TerrainRaiseLimit", settings.TerrainRaiseLimit.ToString()); | ||
243 | xtw.WriteElementString("TerrainLowerLimit", settings.TerrainLowerLimit.ToString()); | ||
244 | xtw.WriteElementString("UseEstateSun", settings.UseEstateSun.ToString()); | ||
245 | xtw.WriteElementString("FixedSun", settings.FixedSun.ToString()); | ||
246 | // XXX: Need to expose interface to get sun phase information from sun module | ||
247 | // xtw.WriteStartElement("SunPhase", | ||
248 | xtw.WriteEndElement(); | ||
249 | |||
250 | xtw.WriteEndElement(); | ||
251 | |||
252 | xtw.Close(); | ||
253 | sw.Close(); | ||
254 | |||
255 | return sw.ToString(); | ||
256 | } | ||
257 | } | ||
258 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs new file mode 100644 index 0000000..506d770 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs | |||
@@ -0,0 +1,195 @@ | |||
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 OpenSim 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.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Text; | ||
32 | using log4net; | ||
33 | |||
34 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
35 | { | ||
36 | /// <summary> | ||
37 | /// Temporary code to do the bare minimum required to read a tar archive for our purposes | ||
38 | /// </summary> | ||
39 | public class TarArchiveReader | ||
40 | { | ||
41 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
42 | |||
43 | public enum TarEntryType | ||
44 | { | ||
45 | TYPE_UNKNOWN = 0, | ||
46 | TYPE_NORMAL_FILE = 1, | ||
47 | TYPE_HARD_LINK = 2, | ||
48 | TYPE_SYMBOLIC_LINK = 3, | ||
49 | TYPE_CHAR_SPECIAL = 4, | ||
50 | TYPE_BLOCK_SPECIAL = 5, | ||
51 | TYPE_DIRECTORY = 6, | ||
52 | TYPE_FIFO = 7, | ||
53 | TYPE_CONTIGUOUS_FILE = 8, | ||
54 | } | ||
55 | |||
56 | protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding(); | ||
57 | |||
58 | /// <summary> | ||
59 | /// Binary reader for the underlying stream | ||
60 | /// </summary> | ||
61 | protected BinaryReader m_br; | ||
62 | |||
63 | /// <summary> | ||
64 | /// Used to trim off null chars | ||
65 | /// </summary> | ||
66 | protected char[] m_nullCharArray = new char[] { '\0' }; | ||
67 | |||
68 | /// <summary> | ||
69 | /// Generate a tar reader which reads from the given stream. | ||
70 | /// </summary> | ||
71 | /// <param name="s"></param> | ||
72 | public TarArchiveReader(Stream s) | ||
73 | { | ||
74 | m_br = new BinaryReader(s); | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Read the next entry in the tar file. | ||
79 | /// </summary> | ||
80 | /// <param name="filePath"></param> | ||
81 | /// <returns>the data for the entry. Returns null if there are no more entries</returns> | ||
82 | public byte[] ReadEntry(out string filePath, out TarEntryType entryType) | ||
83 | { | ||
84 | filePath = String.Empty; | ||
85 | entryType = TarEntryType.TYPE_UNKNOWN; | ||
86 | TarHeader header = ReadHeader(); | ||
87 | |||
88 | if (null == header) | ||
89 | return null; | ||
90 | |||
91 | entryType = header.EntryType; | ||
92 | filePath = header.FilePath; | ||
93 | byte[] data = m_br.ReadBytes(header.FileSize); | ||
94 | |||
95 | //m_log.DebugFormat("[TAR ARCHIVE READER]: filePath {0}, fileSize {1}", filePath, header.FileSize); | ||
96 | |||
97 | // Read the rest of the empty padding in the 512 byte block | ||
98 | if (header.FileSize % 512 != 0) | ||
99 | { | ||
100 | int paddingLeft = 512 - (header.FileSize % 512); | ||
101 | |||
102 | //m_log.DebugFormat("[TAR ARCHIVE READER]: Reading {0} padding bytes", paddingLeft); | ||
103 | |||
104 | m_br.ReadBytes(paddingLeft); | ||
105 | } | ||
106 | |||
107 | return data; | ||
108 | } | ||
109 | |||
110 | /// <summary> | ||
111 | /// Read the next 512 byte chunk of data as a tar header. | ||
112 | /// </summary> | ||
113 | /// <returns>A tar header struct. null if we have reached the end of the archive.</returns> | ||
114 | protected TarHeader ReadHeader() | ||
115 | { | ||
116 | byte[] header = m_br.ReadBytes(512); | ||
117 | |||
118 | // If we've reached the end of the archive we'll be in null block territory, which means | ||
119 | // the next byte will be 0 | ||
120 | if (header[0] == 0) | ||
121 | return null; | ||
122 | |||
123 | TarHeader tarHeader = new TarHeader(); | ||
124 | |||
125 | tarHeader.FilePath = m_asciiEncoding.GetString(header, 0, 100); | ||
126 | tarHeader.FilePath = tarHeader.FilePath.Trim(m_nullCharArray); | ||
127 | tarHeader.FileSize = ConvertOctalBytesToDecimal(header, 124, 11); | ||
128 | |||
129 | switch (header[156]) | ||
130 | { | ||
131 | case 0: | ||
132 | tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE; | ||
133 | break; | ||
134 | case (byte)'0': | ||
135 | tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE; | ||
136 | break; | ||
137 | case (byte)'1': | ||
138 | tarHeader.EntryType = TarEntryType.TYPE_HARD_LINK; | ||
139 | break; | ||
140 | case (byte)'2': | ||
141 | tarHeader.EntryType = TarEntryType.TYPE_SYMBOLIC_LINK; | ||
142 | break; | ||
143 | case (byte)'3': | ||
144 | tarHeader.EntryType = TarEntryType.TYPE_CHAR_SPECIAL; | ||
145 | break; | ||
146 | case (byte)'4': | ||
147 | tarHeader.EntryType = TarEntryType.TYPE_BLOCK_SPECIAL; | ||
148 | break; | ||
149 | case (byte)'5': | ||
150 | tarHeader.EntryType = TarEntryType.TYPE_DIRECTORY; | ||
151 | break; | ||
152 | case (byte)'6': | ||
153 | tarHeader.EntryType = TarEntryType.TYPE_FIFO; | ||
154 | break; | ||
155 | case (byte)'7': | ||
156 | tarHeader.EntryType = TarEntryType.TYPE_CONTIGUOUS_FILE; | ||
157 | break; | ||
158 | } | ||
159 | |||
160 | return tarHeader; | ||
161 | } | ||
162 | |||
163 | public void Close() | ||
164 | { | ||
165 | m_br.Close(); | ||
166 | } | ||
167 | |||
168 | /// <summary> | ||
169 | /// Convert octal bytes to a decimal representation | ||
170 | /// </summary> | ||
171 | /// <param name="bytes"></param> | ||
172 | /// <returns></returns> | ||
173 | public static int ConvertOctalBytesToDecimal(byte[] bytes, int startIndex, int count) | ||
174 | { | ||
175 | string oString = m_asciiEncoding.GetString(bytes, startIndex, count); | ||
176 | |||
177 | int d = 0; | ||
178 | |||
179 | foreach (char c in oString) | ||
180 | { | ||
181 | d <<= 3; | ||
182 | d |= c - '0'; | ||
183 | } | ||
184 | |||
185 | return d; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | public class TarHeader | ||
190 | { | ||
191 | public string FilePath; | ||
192 | public int FileSize; | ||
193 | public TarArchiveReader.TarEntryType EntryType; | ||
194 | } | ||
195 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs new file mode 100644 index 0000000..437939e --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs | |||
@@ -0,0 +1,202 @@ | |||
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 OpenSim 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.Text; | ||
32 | using System.Reflection; | ||
33 | using log4net; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
36 | { | ||
37 | /// <summary> | ||
38 | /// Temporary code to produce a tar archive in tar v7 format | ||
39 | /// </summary> | ||
40 | public class TarArchiveWriter | ||
41 | { | ||
42 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
43 | |||
44 | protected Dictionary<string, byte[]> m_files = new Dictionary<string, byte[]>(); | ||
45 | |||
46 | protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding(); | ||
47 | |||
48 | /// <summary> | ||
49 | /// Add a directory to the tar archive. We can only handle one path level right now! | ||
50 | /// </summary> | ||
51 | /// <param name="dirName"></param> | ||
52 | public void AddDir(string dirName) | ||
53 | { | ||
54 | // Directories are signalled by a final / | ||
55 | if (!dirName.EndsWith("/")) | ||
56 | dirName += "/"; | ||
57 | |||
58 | AddFile(dirName, new byte[0]); | ||
59 | } | ||
60 | |||
61 | /// <summary> | ||
62 | /// Add a file to the tar archive | ||
63 | /// </summary> | ||
64 | /// <param name="filePath"></param> | ||
65 | /// <param name="data"></param> | ||
66 | public void AddFile(string filePath, string data) | ||
67 | { | ||
68 | AddFile(filePath, m_asciiEncoding.GetBytes(data)); | ||
69 | } | ||
70 | |||
71 | /// <summary> | ||
72 | /// Add a file to the tar archive | ||
73 | /// </summary> | ||
74 | /// <param name="filePath"></param> | ||
75 | /// <param name="data"></param> | ||
76 | public void AddFile(string filePath, byte[] data) | ||
77 | { | ||
78 | m_files[filePath] = data; | ||
79 | } | ||
80 | |||
81 | /// <summary> | ||
82 | /// Write the raw tar archive data to a stream. The stream will be closed on completion. | ||
83 | /// </summary> | ||
84 | /// <param name="s">Stream to which to write the data</param> | ||
85 | /// <returns></returns> | ||
86 | public void WriteTar(Stream s) | ||
87 | { | ||
88 | BinaryWriter bw = new BinaryWriter(s); | ||
89 | |||
90 | foreach (string filePath in m_files.Keys) | ||
91 | { | ||
92 | byte[] header = new byte[512]; | ||
93 | byte[] data = m_files[filePath]; | ||
94 | |||
95 | // file path field (100) | ||
96 | byte[] nameBytes = m_asciiEncoding.GetBytes(filePath); | ||
97 | int nameSize = (nameBytes.Length >= 100) ? 100 : nameBytes.Length; | ||
98 | Array.Copy(nameBytes, header, nameSize); | ||
99 | |||
100 | // file mode (8) | ||
101 | byte[] modeBytes = m_asciiEncoding.GetBytes("0000777"); | ||
102 | Array.Copy(modeBytes, 0, header, 100, 7); | ||
103 | |||
104 | // owner user id (8) | ||
105 | byte[] ownerIdBytes = m_asciiEncoding.GetBytes("0000764"); | ||
106 | Array.Copy(ownerIdBytes, 0, header, 108, 7); | ||
107 | |||
108 | // group user id (8) | ||
109 | byte[] groupIdBytes = m_asciiEncoding.GetBytes("0000764"); | ||
110 | Array.Copy(groupIdBytes, 0, header, 116, 7); | ||
111 | |||
112 | // file size in bytes (12) | ||
113 | int fileSize = data.Length; | ||
114 | //m_log.DebugFormat("[TAR ARCHIVE WRITER]: File size of {0} is {1}", filePath, fileSize); | ||
115 | |||
116 | byte[] fileSizeBytes = ConvertDecimalToPaddedOctalBytes(fileSize, 11); | ||
117 | |||
118 | Array.Copy(fileSizeBytes, 0, header, 124, 11); | ||
119 | |||
120 | // last modification time (12) | ||
121 | byte[] lastModTimeBytes = m_asciiEncoding.GetBytes("11017037332"); | ||
122 | Array.Copy(lastModTimeBytes, 0, header, 136, 11); | ||
123 | |||
124 | // link indicator (1) | ||
125 | //header[156] = m_asciiEncoding.GetBytes("0")[0]; | ||
126 | if (filePath.EndsWith("/")) | ||
127 | { | ||
128 | header[156] = m_asciiEncoding.GetBytes("5")[0]; | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | header[156] = 0; | ||
133 | } | ||
134 | |||
135 | Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 329, 7); | ||
136 | Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 337, 7); | ||
137 | |||
138 | // check sum for header block (8) [calculated last] | ||
139 | Array.Copy(m_asciiEncoding.GetBytes(" "), 0, header, 148, 8); | ||
140 | |||
141 | int checksum = 0; | ||
142 | foreach (byte b in header) | ||
143 | { | ||
144 | checksum += b; | ||
145 | } | ||
146 | |||
147 | //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Decimal header checksum is {0}", checksum); | ||
148 | |||
149 | byte[] checkSumBytes = ConvertDecimalToPaddedOctalBytes(checksum, 6); | ||
150 | |||
151 | Array.Copy(checkSumBytes, 0, header, 148, 6); | ||
152 | |||
153 | header[154] = 0; | ||
154 | |||
155 | // Write out header | ||
156 | bw.Write(header); | ||
157 | |||
158 | // Write out data | ||
159 | bw.Write(data); | ||
160 | |||
161 | if (data.Length % 512 != 0) | ||
162 | { | ||
163 | int paddingRequired = 512 - (data.Length % 512); | ||
164 | |||
165 | //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Padding data with {0} bytes", paddingRequired); | ||
166 | |||
167 | byte[] padding = new byte[paddingRequired]; | ||
168 | bw.Write(padding); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | //m_log.Debug("[TAR ARCHIVE WRITER]: Writing final consecutive 0 blocks"); | ||
173 | |||
174 | // Write two consecutive 0 blocks to end the archive | ||
175 | byte[] finalZeroPadding = new byte[1024]; | ||
176 | bw.Write(finalZeroPadding); | ||
177 | |||
178 | bw.Flush(); | ||
179 | bw.Close(); | ||
180 | } | ||
181 | |||
182 | public static byte[] ConvertDecimalToPaddedOctalBytes(int d, int padding) | ||
183 | { | ||
184 | string oString = ""; | ||
185 | |||
186 | while (d > 0) | ||
187 | { | ||
188 | oString = Convert.ToString((byte)'0' + d & 7) + oString; | ||
189 | d >>= 3; | ||
190 | } | ||
191 | |||
192 | while (oString.Length < padding) | ||
193 | { | ||
194 | oString = "0" + oString; | ||
195 | } | ||
196 | |||
197 | byte[] oBytes = m_asciiEncoding.GetBytes(oString); | ||
198 | |||
199 | return oBytes; | ||
200 | } | ||
201 | } | ||
202 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs new file mode 100644 index 0000000..a14e0f6 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs | |||
@@ -0,0 +1,188 @@ | |||
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 OpenSim 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.IO; | ||
30 | using System.Threading; | ||
31 | using NUnit.Framework; | ||
32 | using NUnit.Framework.SyntaxHelpers; | ||
33 | using OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.CoreModules.World.Archiver; | ||
37 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
38 | using OpenSim.Region.CoreModules.World.Terrain; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using OpenSim.Tests.Common.Setup; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.World.Archiver.Tests | ||
43 | { | ||
44 | [TestFixture] | ||
45 | public class ArchiverTests | ||
46 | { | ||
47 | private EventWaitHandle m_waitHandle = new AutoResetEvent(false); | ||
48 | |||
49 | private void SaveCompleted(string errorMessage) | ||
50 | { | ||
51 | m_waitHandle.Set(); | ||
52 | } | ||
53 | |||
54 | /// <summary> | ||
55 | /// Test saving a V0.2 OpenSim Region Archive. | ||
56 | /// </summary> | ||
57 | [Test] | ||
58 | public void TestSaveOarV0p2() | ||
59 | { | ||
60 | log4net.Config.XmlConfigurator.Configure(); | ||
61 | |||
62 | ArchiverModule archiverModule = new ArchiverModule(); | ||
63 | SerialiserModule serialiserModule = new SerialiserModule(); | ||
64 | TerrainModule terrainModule = new TerrainModule(); | ||
65 | |||
66 | Scene scene = SceneSetupHelpers.SetupScene(); | ||
67 | SceneSetupHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule); | ||
68 | |||
69 | SceneObjectPart part1; | ||
70 | |||
71 | // Create and add prim 1 | ||
72 | { | ||
73 | string partName = "My Little Pony"; | ||
74 | UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000015"); | ||
75 | PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); | ||
76 | Vector3 groupPosition = new Vector3(10, 20, 30); | ||
77 | Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); | ||
78 | Vector3 offsetPosition = new Vector3(5, 10, 15); | ||
79 | |||
80 | part1 | ||
81 | = new SceneObjectPart( | ||
82 | ownerId, shape, groupPosition, rotationOffset, offsetPosition); | ||
83 | part1.Name = partName; | ||
84 | |||
85 | scene.AddNewSceneObject(new SceneObjectGroup(part1), false); | ||
86 | } | ||
87 | |||
88 | SceneObjectPart part2; | ||
89 | |||
90 | // Create and add prim 2 | ||
91 | { | ||
92 | string partName = "Action Man"; | ||
93 | UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000016"); | ||
94 | PrimitiveBaseShape shape = PrimitiveBaseShape.CreateCylinder(); | ||
95 | Vector3 groupPosition = new Vector3(90, 80, 70); | ||
96 | Quaternion rotationOffset = new Quaternion(60, 70, 80, 90); | ||
97 | Vector3 offsetPosition = new Vector3(20, 25, 30); | ||
98 | |||
99 | part2 | ||
100 | = new SceneObjectPart( | ||
101 | ownerId, shape, groupPosition, rotationOffset, offsetPosition); | ||
102 | part2.Name = partName; | ||
103 | |||
104 | scene.AddNewSceneObject(new SceneObjectGroup(part2), false); | ||
105 | } | ||
106 | |||
107 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
108 | |||
109 | scene.EventManager.OnOarFileSaved += SaveCompleted; | ||
110 | archiverModule.ArchiveRegion(archiveWriteStream); | ||
111 | m_waitHandle.WaitOne(60000, true); | ||
112 | |||
113 | byte[] archive = archiveWriteStream.ToArray(); | ||
114 | MemoryStream archiveReadStream = new MemoryStream(archive); | ||
115 | TarArchiveReader tar = new TarArchiveReader(archiveReadStream); | ||
116 | |||
117 | bool gotControlFile = false; | ||
118 | bool gotObject1File = false; | ||
119 | bool gotObject2File = false; | ||
120 | string expectedObject1FileName = string.Format( | ||
121 | "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", | ||
122 | part1.Name, | ||
123 | Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z), | ||
124 | part1.UUID); | ||
125 | string expectedObject2FileName = string.Format( | ||
126 | "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", | ||
127 | part2.Name, | ||
128 | Math.Round(part2.GroupPosition.X), Math.Round(part2.GroupPosition.Y), Math.Round(part2.GroupPosition.Z), | ||
129 | part2.UUID); | ||
130 | |||
131 | string filePath; | ||
132 | TarArchiveReader.TarEntryType tarEntryType; | ||
133 | |||
134 | while (tar.ReadEntry(out filePath, out tarEntryType) != null) | ||
135 | { | ||
136 | if (ArchiveConstants.CONTROL_FILE_PATH == filePath) | ||
137 | { | ||
138 | gotControlFile = true; | ||
139 | } | ||
140 | else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) | ||
141 | { | ||
142 | string fileName = filePath.Remove(0, ArchiveConstants.OBJECTS_PATH.Length); | ||
143 | |||
144 | if (fileName.StartsWith(part1.Name)) | ||
145 | { | ||
146 | Assert.That(fileName, Is.EqualTo(expectedObject1FileName)); | ||
147 | gotObject1File = true; | ||
148 | } | ||
149 | else if (fileName.StartsWith(part2.Name)) | ||
150 | { | ||
151 | Assert.That(fileName, Is.EqualTo(expectedObject2FileName)); | ||
152 | gotObject2File = true; | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | Assert.That(gotControlFile, Is.True, "No control file in archive"); | ||
158 | Assert.That(gotObject1File, Is.True, "No object1 file in archive"); | ||
159 | Assert.That(gotObject2File, Is.True, "No object2 file in archive"); | ||
160 | |||
161 | // TODO: Test presence of more files and contents of files. | ||
162 | } | ||
163 | |||
164 | /// <summary> | ||
165 | /// Test loading a V0.2 OpenSim Region Archive. Does not yet do what it says on the tin. | ||
166 | /// </summary> | ||
167 | [Test] | ||
168 | public void TestLoadOarV0p2() | ||
169 | { | ||
170 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
171 | TarArchiveWriter tar = new TarArchiveWriter(); | ||
172 | |||
173 | tar.AddFile(ArchiveConstants.CONTROL_FILE_PATH, ArchiveWriteRequestExecution.Create0p2ControlFile()); | ||
174 | tar.WriteTar(archiveWriteStream); | ||
175 | |||
176 | MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray()); | ||
177 | |||
178 | ArchiverModule archiverModule = new ArchiverModule(); | ||
179 | |||
180 | Scene scene = SceneSetupHelpers.SetupScene(); | ||
181 | SceneSetupHelpers.SetupSceneModules(scene, archiverModule); | ||
182 | |||
183 | archiverModule.DearchiveRegion(archiveReadStream); | ||
184 | |||
185 | // TODO: Okay, so nothing is tested yet apart from the fact that it doesn't blow up | ||
186 | } | ||
187 | } | ||
188 | } \ No newline at end of file | ||