aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Archiver
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Archiver')
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs128
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs460
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs161
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs333
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs95
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs143
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs184
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs138
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs258
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs195
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs202
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs188
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
28using System.Collections.Generic;
29using OpenMetaverse;
30
31namespace 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.IO.Compression;
32using System.Reflection;
33using System.Xml;
34using System.Net;
35using OpenMetaverse;
36using log4net;
37using OpenSim.Framework;
38using OpenSim.Framework.Communications.Cache;
39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes;
41using OpenSim.Region.CoreModules.World.Terrain;
42
43namespace 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32using System.Xml;
33using OpenMetaverse;
34using log4net;
35using OpenSim.Framework;
36using OpenSim.Region.Framework.Interfaces;
37using OpenSim.Region.Framework.Scenes;
38using OpenSim.Region.CoreModules.World.Serialiser;
39using OpenSim.Region.CoreModules.World.Terrain;
40
41namespace 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
28using OpenSim.Framework;
29using OpenSim.Framework.Communications.Cache;
30using OpenSim.Region.Framework.Interfaces;
31using OpenSim.Region.Framework.Scenes;
32using OpenSim.Region.CoreModules.World.Serialiser;
33using OpenSim.Region.CoreModules.World.Terrain;
34using System;
35using System.Collections.Generic;
36using System.IO;
37using System.IO.Compression;
38using System.Reflection;
39using System.Text.RegularExpressions;
40using System.Threading;
41using OpenMetaverse;
42using log4net;
43using Nini.Config;
44
45namespace 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
28using System.Collections.Generic;
29using System.IO;
30using System.Reflection;
31using System.Threading;
32using OpenMetaverse;
33using log4net;
34using Nini.Config;
35using OpenSim.Framework.Communications.Cache;
36using OpenSim.Region.Framework.Interfaces;
37using OpenSim.Region.Framework.Scenes;
38using OpenSim.Region.CoreModules.World.Serialiser;
39
40namespace 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
28using System.Collections.Generic;
29using System.IO;
30using System.Reflection;
31using System.Xml;
32using OpenMetaverse;
33using log4net;
34using OpenSim.Framework;
35
36namespace 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32using System.Xml;
33using OpenMetaverse;
34using log4net;
35using OpenSim.Framework;
36using OpenSim.Framework.Communications.Cache;
37
38namespace 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
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Threading;
32using OpenMetaverse;
33using log4net;
34using OpenSim.Framework;
35using OpenSim.Framework.Communications.Cache;
36using OpenSim.Region.Framework.Interfaces;
37using OpenSim.Region.Framework.Scenes;
38
39namespace 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
28using System;
29using System.IO;
30using System.Text;
31using System.Xml;
32using OpenMetaverse;
33using OpenSim.Framework;
34
35namespace 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
28using System;
29using System.IO;
30using System.Reflection;
31using System.Text;
32using log4net;
33
34namespace 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Text;
32using System.Reflection;
33using log4net;
34
35namespace 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
28using System;
29using System.IO;
30using System.Threading;
31using NUnit.Framework;
32using NUnit.Framework.SyntaxHelpers;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.CoreModules.World.Archiver;
37using OpenSim.Region.CoreModules.World.Serialiser;
38using OpenSim.Region.CoreModules.World.Terrain;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Tests.Common.Setup;
41
42namespace 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