diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs new file mode 100644 index 0000000..3218abc --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs | |||
@@ -0,0 +1,460 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.IO.Compression; | ||
32 | using System.Reflection; | ||
33 | using System.Xml; | ||
34 | using System.Net; | ||
35 | using OpenMetaverse; | ||
36 | using log4net; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Framework.Communications.Cache; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | using OpenSim.Region.CoreModules.World.Terrain; | ||
42 | |||
43 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
44 | { | ||
45 | /// <summary> | ||
46 | /// Handles an individual archive read request | ||
47 | /// </summary> | ||
48 | public class ArchiveReadRequest | ||
49 | { | ||
50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
51 | |||
52 | private static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding(); | ||
53 | |||
54 | private Scene m_scene; | ||
55 | private Stream m_loadStream; | ||
56 | private string m_errorMessage; | ||
57 | |||
58 | /// <summary> | ||
59 | /// Used to cache lookups for valid uuids. | ||
60 | /// </summary> | ||
61 | private IDictionary<UUID, bool> m_validUserUuids = new Dictionary<UUID, bool>(); | ||
62 | |||
63 | public ArchiveReadRequest(Scene scene, string loadPath) | ||
64 | { | ||
65 | m_scene = scene; | ||
66 | m_loadStream = new GZipStream(GetStream(loadPath), CompressionMode.Decompress); | ||
67 | m_errorMessage = String.Empty; | ||
68 | } | ||
69 | |||
70 | public ArchiveReadRequest(Scene scene, Stream loadStream) | ||
71 | { | ||
72 | m_scene = scene; | ||
73 | m_loadStream = loadStream; | ||
74 | } | ||
75 | |||
76 | /// <summary> | ||
77 | /// Dearchive the region embodied in this request. | ||
78 | /// </summary> | ||
79 | public void DearchiveRegion() | ||
80 | { | ||
81 | // The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions | ||
82 | DearchiveRegion0DotStar(); | ||
83 | } | ||
84 | |||
85 | private void DearchiveRegion0DotStar() | ||
86 | { | ||
87 | int successfulAssetRestores = 0; | ||
88 | int failedAssetRestores = 0; | ||
89 | List<string> serialisedSceneObjects = new List<string>(); | ||
90 | |||
91 | try | ||
92 | { | ||
93 | TarArchiveReader archive = new TarArchiveReader(m_loadStream); | ||
94 | |||
95 | //AssetsDearchiver dearchiver = new AssetsDearchiver(m_scene.AssetCache); | ||
96 | |||
97 | string filePath = "ERROR"; | ||
98 | |||
99 | byte[] data; | ||
100 | TarArchiveReader.TarEntryType entryType; | ||
101 | |||
102 | while ((data = archive.ReadEntry(out filePath, out entryType)) != null) | ||
103 | { | ||
104 | //m_log.DebugFormat( | ||
105 | // "[ARCHIVER]: Successfully read {0} ({1} bytes)}", filePath, data.Length); | ||
106 | if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType) | ||
107 | { | ||
108 | m_log.WarnFormat("[ARCHIVER]: Ignoring directory entry {0}", | ||
109 | filePath); | ||
110 | } | ||
111 | else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) | ||
112 | { | ||
113 | serialisedSceneObjects.Add(m_asciiEncoding.GetString(data)); | ||
114 | } | ||
115 | // else if (filePath.Equals(ArchiveConstants.ASSETS_METADATA_PATH)) | ||
116 | // { | ||
117 | // string xml = m_asciiEncoding.GetString(data); | ||
118 | // dearchiver.AddAssetMetadata(xml); | ||
119 | // } | ||
120 | else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) | ||
121 | { | ||
122 | if (LoadAsset(filePath, data)) | ||
123 | successfulAssetRestores++; | ||
124 | else | ||
125 | failedAssetRestores++; | ||
126 | } | ||
127 | else if (filePath.StartsWith(ArchiveConstants.TERRAINS_PATH)) | ||
128 | { | ||
129 | LoadTerrain(filePath, data); | ||
130 | } | ||
131 | else if (filePath.StartsWith(ArchiveConstants.SETTINGS_PATH)) | ||
132 | { | ||
133 | LoadRegionSettings(filePath, data); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | //m_log.Debug("[ARCHIVER]: Reached end of archive"); | ||
138 | |||
139 | archive.Close(); | ||
140 | } | ||
141 | catch (Exception e) | ||
142 | { | ||
143 | m_log.ErrorFormat( | ||
144 | "[ARCHIVER]: Error loading oar file. Exception was: {0}", e); | ||
145 | m_errorMessage += e.ToString(); | ||
146 | m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage); | ||
147 | return; | ||
148 | } | ||
149 | |||
150 | m_log.InfoFormat("[ARCHIVER]: Restored {0} assets", successfulAssetRestores); | ||
151 | |||
152 | if (failedAssetRestores > 0) | ||
153 | { | ||
154 | m_log.ErrorFormat("[ARCHIVER]: Failed to load {0} assets", failedAssetRestores); | ||
155 | m_errorMessage += String.Format("Failed to load {0} assets", failedAssetRestores); | ||
156 | } | ||
157 | |||
158 | m_log.Info("[ARCHIVER]: Clearing all existing scene objects"); | ||
159 | m_scene.DeleteAllSceneObjects(); | ||
160 | |||
161 | // Reload serialized prims | ||
162 | m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count); | ||
163 | |||
164 | IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface<IRegionSerialiserModule>(); | ||
165 | ICollection<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | ||
166 | |||
167 | foreach (string serialisedSceneObject in serialisedSceneObjects) | ||
168 | { | ||
169 | SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject); | ||
170 | |||
171 | // For now, give all incoming scene objects new uuids. This will allow scenes to be cloned | ||
172 | // on the same region server and multiple examples a single object archive to be imported | ||
173 | // to the same scene (when this is possible). | ||
174 | sceneObject.ResetIDs(); | ||
175 | |||
176 | // Try to retain the original creator/owner/lastowner if their uuid is present on this grid | ||
177 | // otherwise, use the master avatar uuid instead | ||
178 | UUID masterAvatarId = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
179 | |||
180 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
181 | masterAvatarId = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
182 | |||
183 | foreach (SceneObjectPart part in sceneObject.Children.Values) | ||
184 | { | ||
185 | if (!resolveUserUuid(part.CreatorID)) | ||
186 | part.CreatorID = masterAvatarId; | ||
187 | |||
188 | if (!resolveUserUuid(part.OwnerID)) | ||
189 | part.OwnerID = masterAvatarId; | ||
190 | |||
191 | if (!resolveUserUuid(part.LastOwnerID)) | ||
192 | part.LastOwnerID = masterAvatarId; | ||
193 | |||
194 | // And zap any troublesome sit target information | ||
195 | part.SitTargetOrientation = new Quaternion(0, 0, 0, 1); | ||
196 | part.SitTargetPosition = new Vector3(0, 0, 0); | ||
197 | |||
198 | // Fix ownership/creator of inventory items | ||
199 | // Not doing so results in inventory items | ||
200 | // being no copy/no mod for everyone | ||
201 | TaskInventoryDictionary inv = part.TaskInventory; | ||
202 | foreach (KeyValuePair<UUID, TaskInventoryItem> kvp in inv) | ||
203 | { | ||
204 | if (!resolveUserUuid(kvp.Value.OwnerID)) | ||
205 | { | ||
206 | kvp.Value.OwnerID = masterAvatarId; | ||
207 | } | ||
208 | if (!resolveUserUuid(kvp.Value.CreatorID)) | ||
209 | { | ||
210 | kvp.Value.CreatorID = masterAvatarId; | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | if (m_scene.AddRestoredSceneObject(sceneObject, true, false)) | ||
216 | { | ||
217 | sceneObjects.Add(sceneObject); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | m_log.InfoFormat("[ARCHIVER]: Restored {0} scene objects to the scene", sceneObjects.Count); | ||
222 | |||
223 | int ignoredObjects = serialisedSceneObjects.Count - sceneObjects.Count; | ||
224 | |||
225 | if (ignoredObjects > 0) | ||
226 | m_log.WarnFormat("[ARCHIVER]: Ignored {0} scene objects that already existed in the scene", ignoredObjects); | ||
227 | |||
228 | m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive"); | ||
229 | |||
230 | m_log.Debug("[ARCHIVER]: Starting scripts"); | ||
231 | |||
232 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
233 | { | ||
234 | sceneObject.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 0); | ||
235 | } | ||
236 | |||
237 | m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage); | ||
238 | } | ||
239 | |||
240 | /// <summary> | ||
241 | /// Look up the given user id to check whether it's one that is valid for this grid. | ||
242 | /// </summary> | ||
243 | /// <param name="uuid"></param> | ||
244 | /// <returns></returns> | ||
245 | private bool resolveUserUuid(UUID uuid) | ||
246 | { | ||
247 | if (!m_validUserUuids.ContainsKey(uuid)) | ||
248 | { | ||
249 | CachedUserInfo profile = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(uuid); | ||
250 | if (profile != null && profile.UserProfile != null) | ||
251 | m_validUserUuids.Add(uuid, true); | ||
252 | else | ||
253 | m_validUserUuids.Add(uuid, false); | ||
254 | } | ||
255 | |||
256 | if (m_validUserUuids[uuid]) | ||
257 | return true; | ||
258 | else | ||
259 | return false; | ||
260 | } | ||
261 | |||
262 | /// <summary> | ||
263 | /// Load an asset | ||
264 | /// </summary> | ||
265 | /// <param name="assetFilename"></param> | ||
266 | /// <param name="data"></param> | ||
267 | /// <returns>true if asset was successfully loaded, false otherwise</returns> | ||
268 | private bool LoadAsset(string assetPath, byte[] data) | ||
269 | { | ||
270 | // Right now we're nastily obtaining the UUID from the filename | ||
271 | string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length); | ||
272 | int i = filename.LastIndexOf(ArchiveConstants.ASSET_EXTENSION_SEPARATOR); | ||
273 | |||
274 | if (i == -1) | ||
275 | { | ||
276 | m_log.ErrorFormat( | ||
277 | "[ARCHIVER]: Could not find extension information in asset path {0} since it's missing the separator {1}. Skipping", | ||
278 | assetPath, ArchiveConstants.ASSET_EXTENSION_SEPARATOR); | ||
279 | |||
280 | return false; | ||
281 | } | ||
282 | |||
283 | string extension = filename.Substring(i); | ||
284 | string uuid = filename.Remove(filename.Length - extension.Length); | ||
285 | |||
286 | if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension)) | ||
287 | { | ||
288 | sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension]; | ||
289 | |||
290 | //m_log.DebugFormat("[ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType); | ||
291 | |||
292 | AssetBase asset = new AssetBase(new UUID(uuid), String.Empty); | ||
293 | asset.Metadata.Type = assetType; | ||
294 | asset.Data = data; | ||
295 | |||
296 | m_scene.AssetCache.AddAsset(asset); | ||
297 | |||
298 | /** | ||
299 | * Create layers on decode for image assets. This is likely to significantly increase the time to load archives so | ||
300 | * it might be best done when dearchive takes place on a separate thread | ||
301 | if (asset.Type=AssetType.Texture) | ||
302 | { | ||
303 | IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>(); | ||
304 | if (cacheLayerDecode != null) | ||
305 | cacheLayerDecode.syncdecode(asset.FullID, asset.Data); | ||
306 | } | ||
307 | */ | ||
308 | |||
309 | return true; | ||
310 | } | ||
311 | else | ||
312 | { | ||
313 | m_log.ErrorFormat( | ||
314 | "[ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}", | ||
315 | assetPath, extension); | ||
316 | |||
317 | return false; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | /// <summary> | ||
322 | /// Load region settings data | ||
323 | /// </summary> | ||
324 | /// <param name="settingsPath"></param> | ||
325 | /// <param name="data"></param> | ||
326 | /// <returns> | ||
327 | /// true if settings were loaded successfully, false otherwise | ||
328 | /// </returns> | ||
329 | private bool LoadRegionSettings(string settingsPath, byte[] data) | ||
330 | { | ||
331 | RegionSettings loadedRegionSettings; | ||
332 | |||
333 | try | ||
334 | { | ||
335 | loadedRegionSettings = RegionSettingsSerializer.Deserialize(data); | ||
336 | } | ||
337 | catch (Exception e) | ||
338 | { | ||
339 | m_log.ErrorFormat( | ||
340 | "[ARCHIVER]: Could not parse region settings file {0}. Ignoring. Exception was {1}", | ||
341 | settingsPath, e); | ||
342 | return false; | ||
343 | } | ||
344 | |||
345 | RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings; | ||
346 | |||
347 | currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit; | ||
348 | currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage; | ||
349 | currentRegionSettings.AllowLandJoinDivide = loadedRegionSettings.AllowLandJoinDivide; | ||
350 | currentRegionSettings.AllowLandResell = loadedRegionSettings.AllowLandResell; | ||
351 | currentRegionSettings.BlockFly = loadedRegionSettings.BlockFly; | ||
352 | currentRegionSettings.BlockShowInSearch = loadedRegionSettings.BlockShowInSearch; | ||
353 | currentRegionSettings.BlockTerraform = loadedRegionSettings.BlockTerraform; | ||
354 | currentRegionSettings.DisableCollisions = loadedRegionSettings.DisableCollisions; | ||
355 | currentRegionSettings.DisablePhysics = loadedRegionSettings.DisablePhysics; | ||
356 | currentRegionSettings.DisableScripts = loadedRegionSettings.DisableScripts; | ||
357 | currentRegionSettings.Elevation1NE = loadedRegionSettings.Elevation1NE; | ||
358 | currentRegionSettings.Elevation1NW = loadedRegionSettings.Elevation1NW; | ||
359 | currentRegionSettings.Elevation1SE = loadedRegionSettings.Elevation1SE; | ||
360 | currentRegionSettings.Elevation1SW = loadedRegionSettings.Elevation1SW; | ||
361 | currentRegionSettings.Elevation2NE = loadedRegionSettings.Elevation2NE; | ||
362 | currentRegionSettings.Elevation2NW = loadedRegionSettings.Elevation2NW; | ||
363 | currentRegionSettings.Elevation2SE = loadedRegionSettings.Elevation2SE; | ||
364 | currentRegionSettings.Elevation2SW = loadedRegionSettings.Elevation2SW; | ||
365 | currentRegionSettings.FixedSun = loadedRegionSettings.FixedSun; | ||
366 | currentRegionSettings.ObjectBonus = loadedRegionSettings.ObjectBonus; | ||
367 | currentRegionSettings.RestrictPushing = loadedRegionSettings.RestrictPushing; | ||
368 | currentRegionSettings.TerrainLowerLimit = loadedRegionSettings.TerrainLowerLimit; | ||
369 | currentRegionSettings.TerrainRaiseLimit = loadedRegionSettings.TerrainRaiseLimit; | ||
370 | currentRegionSettings.TerrainTexture1 = loadedRegionSettings.TerrainTexture1; | ||
371 | currentRegionSettings.TerrainTexture2 = loadedRegionSettings.TerrainTexture2; | ||
372 | currentRegionSettings.TerrainTexture3 = loadedRegionSettings.TerrainTexture3; | ||
373 | currentRegionSettings.TerrainTexture4 = loadedRegionSettings.TerrainTexture4; | ||
374 | currentRegionSettings.UseEstateSun = loadedRegionSettings.UseEstateSun; | ||
375 | currentRegionSettings.WaterHeight = loadedRegionSettings.WaterHeight; | ||
376 | |||
377 | IEstateModule estateModule = m_scene.RequestModuleInterface<IEstateModule>(); | ||
378 | estateModule.sendRegionHandshakeToAll(); | ||
379 | |||
380 | return true; | ||
381 | } | ||
382 | |||
383 | /// <summary> | ||
384 | /// Load terrain data | ||
385 | /// </summary> | ||
386 | /// <param name="terrainPath"></param> | ||
387 | /// <param name="data"></param> | ||
388 | /// <returns> | ||
389 | /// true if terrain was resolved successfully, false otherwise. | ||
390 | /// </returns> | ||
391 | private bool LoadTerrain(string terrainPath, byte[] data) | ||
392 | { | ||
393 | ITerrainModule terrainModule = m_scene.RequestModuleInterface<ITerrainModule>(); | ||
394 | |||
395 | MemoryStream ms = new MemoryStream(data); | ||
396 | terrainModule.LoadFromStream(terrainPath, ms); | ||
397 | ms.Close(); | ||
398 | |||
399 | m_log.DebugFormat("[ARCHIVER]: Restored terrain {0}", terrainPath); | ||
400 | |||
401 | return true; | ||
402 | } | ||
403 | |||
404 | /// <summary> | ||
405 | /// Resolve path to a working FileStream | ||
406 | /// </summary> | ||
407 | private Stream GetStream(string path) | ||
408 | { | ||
409 | try | ||
410 | { | ||
411 | if (File.Exists(path)) | ||
412 | { | ||
413 | return new FileStream(path, FileMode.Open); | ||
414 | } | ||
415 | else | ||
416 | { | ||
417 | Uri uri = new Uri(path); // throw exception if not valid URI | ||
418 | if (uri.Scheme == "file") | ||
419 | { | ||
420 | return new FileStream(uri.AbsolutePath, FileMode.Open); | ||
421 | } | ||
422 | else | ||
423 | { | ||
424 | if (uri.Scheme != "http") | ||
425 | throw new Exception(String.Format("Unsupported URI scheme ({0})", path)); | ||
426 | |||
427 | // OK, now we know we have an HTTP URI to work with | ||
428 | |||
429 | return URIFetch(uri); | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | catch (Exception e) | ||
434 | { | ||
435 | throw new Exception(String.Format("Unable to create file input stream for {0}: {1}", path, e)); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | private static Stream URIFetch(Uri uri) | ||
440 | { | ||
441 | HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); | ||
442 | |||
443 | // request.Credentials = credentials; | ||
444 | |||
445 | request.ContentLength = 0; | ||
446 | |||
447 | WebResponse response = request.GetResponse(); | ||
448 | Stream file = response.GetResponseStream(); | ||
449 | |||
450 | if (response.ContentType != "application/x-oar") | ||
451 | throw new Exception(String.Format("{0} does not identify an OAR file", uri.ToString())); | ||
452 | |||
453 | if (response.ContentLength == 0) | ||
454 | throw new Exception(String.Format("{0} returned an empty file", uri.ToString())); | ||
455 | |||
456 | // return new BufferedStream(file, (int) response.ContentLength); | ||
457 | return new BufferedStream(file, 1000000); | ||
458 | } | ||
459 | } | ||
460 | } | ||