FSAssetService.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using log4net;
  28. using Nini.Config;
  29. using OpenMetaverse;
  30. using OpenSim.Data;
  31. using OpenSim.Framework;
  32. using OpenSim.Framework.Serialization.External;
  33. using OpenSim.Services.Base;
  34. using OpenSim.Services.Interfaces;
  35. using System;
  36. using System.Collections.Generic;
  37. using System.IO;
  38. using System.IO.Compression;
  39. using System.Reflection;
  40. using System.Security.Cryptography;
  41. using System.Threading;
  42. namespace OpenSim.Services.FSAssetService
  43. {
  44. public class FSAssetConnector : ServiceBase, IAssetService
  45. {
  46. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  47. static System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
  48. static byte[] ToCString(string s)
  49. {
  50. byte[] ret = enc.GetBytes(s);
  51. Array.Resize(ref ret, ret.Length + 1);
  52. ret[ret.Length - 1] = 0;
  53. return ret;
  54. }
  55. protected IAssetLoader m_AssetLoader = null;
  56. protected IFSAssetDataPlugin m_DataConnector = null;
  57. protected IAssetService m_FallbackService;
  58. protected Thread m_WriterThread;
  59. protected Thread m_StatsThread;
  60. protected string m_SpoolDirectory;
  61. protected object m_readLock = new object();
  62. protected object m_statsLock = new object();
  63. protected int m_readCount = 0;
  64. protected int m_readTicks = 0;
  65. protected int m_missingAssets = 0;
  66. protected int m_missingAssetsFS = 0;
  67. protected string m_FSBase;
  68. protected bool m_useOsgridFormat = false;
  69. protected bool m_showStats = true;
  70. private static bool m_mainInitialized;
  71. private static object m_initLock = new object();
  72. private bool m_isMainInstance;
  73. public FSAssetConnector(IConfigSource config)
  74. : this(config, "AssetService")
  75. {
  76. }
  77. public FSAssetConnector(IConfigSource config, string configName) : base(config)
  78. {
  79. IConfig assetConfig = config.Configs[configName];
  80. if (assetConfig == null)
  81. throw new Exception("No AssetService configuration");
  82. lock (m_initLock)
  83. {
  84. if (!m_mainInitialized)
  85. {
  86. m_mainInitialized = true;
  87. m_isMainInstance = !assetConfig.GetBoolean("SecondaryInstance", false);
  88. MainConsole.Instance.Commands.AddCommand("fs", false,
  89. "show assets", "show assets", "Show asset stats",
  90. HandleShowAssets);
  91. MainConsole.Instance.Commands.AddCommand("fs", false,
  92. "show digest", "show digest <ID>", "Show asset digest",
  93. HandleShowDigest);
  94. MainConsole.Instance.Commands.AddCommand("fs", false,
  95. "delete asset", "delete asset <ID>",
  96. "Delete asset from database",
  97. HandleDeleteAsset);
  98. MainConsole.Instance.Commands.AddCommand("fs", false,
  99. "import", "import <conn> <table> [<start> <count>]",
  100. "Import legacy assets",
  101. HandleImportAssets);
  102. MainConsole.Instance.Commands.AddCommand("fs", false,
  103. "force import", "force import <conn> <table> [<start> <count>]",
  104. "Import legacy assets, overwriting current content",
  105. HandleImportAssets);
  106. }
  107. else
  108. {
  109. m_isMainInstance = false; // yes redundant...
  110. }
  111. }
  112. // Get Database Connector from Asset Config (If present)
  113. string dllName = assetConfig.GetString("StorageProvider", string.Empty);
  114. string connectionString = assetConfig.GetString("ConnectionString", string.Empty);
  115. string realm = assetConfig.GetString("Realm", "fsassets");
  116. int SkipAccessTimeDays = assetConfig.GetInt("DaysBetweenAccessTimeUpdates", 0);
  117. // If not found above, fallback to Database defaults
  118. IConfig dbConfig = config.Configs["DatabaseService"];
  119. if (dbConfig != null)
  120. {
  121. if (dllName.Length == 0)
  122. dllName = dbConfig.GetString("StorageProvider", String.Empty);
  123. if (connectionString.Length == 0)
  124. connectionString = dbConfig.GetString("ConnectionString", String.Empty);
  125. }
  126. // No databse connection found in either config
  127. if (string.IsNullOrEmpty(dllName))
  128. throw new Exception("No StorageProvider configured");
  129. if (string.IsNullOrEmpty(connectionString))
  130. throw new Exception("Missing database connection string");
  131. // Create Storage Provider
  132. m_DataConnector = LoadPlugin<IFSAssetDataPlugin>(dllName);
  133. if (m_DataConnector == null)
  134. throw new Exception(string.Format("Could not find a storage interface in the module {0}", dllName));
  135. // Initialize DB And perform any migrations required
  136. m_DataConnector.Initialise(connectionString, realm, SkipAccessTimeDays);
  137. // Setup Fallback Service
  138. string str = assetConfig.GetString("FallbackService", string.Empty);
  139. if (str.Length > 0)
  140. {
  141. object[] args = new object[] { config };
  142. m_FallbackService = LoadPlugin<IAssetService>(str, args);
  143. if (m_FallbackService != null)
  144. {
  145. m_log.Info("[FSASSETS]: Fallback service loaded");
  146. }
  147. else
  148. {
  149. m_log.Error("[FSASSETS]: Failed to load fallback service");
  150. }
  151. }
  152. // Setup directory structure including temp directory
  153. m_SpoolDirectory = assetConfig.GetString("SpoolDirectory", "/tmp");
  154. string spoolTmp = Path.Combine(m_SpoolDirectory, "spool");
  155. Directory.CreateDirectory(spoolTmp);
  156. m_FSBase = assetConfig.GetString("BaseDirectory", String.Empty);
  157. if (m_FSBase.Length == 0)
  158. {
  159. m_log.ErrorFormat("[FSASSETS]: BaseDirectory not specified");
  160. throw new Exception("Configuration error");
  161. }
  162. m_useOsgridFormat = assetConfig.GetBoolean("UseOsgridFormat", m_useOsgridFormat);
  163. // Default is to show stats to retain original behaviour
  164. m_showStats = assetConfig.GetBoolean("ShowConsoleStats", m_showStats);
  165. if (m_isMainInstance)
  166. {
  167. string loader = assetConfig.GetString("DefaultAssetLoader", string.Empty);
  168. if (loader.Length > 0)
  169. {
  170. m_AssetLoader = LoadPlugin<IAssetLoader>(loader);
  171. string loaderArgs = assetConfig.GetString("AssetLoaderArgs", string.Empty);
  172. m_log.InfoFormat("[FSASSETS]: Loading default asset set from {0}", loaderArgs);
  173. m_AssetLoader.ForEachDefaultXmlAsset(loaderArgs,
  174. delegate(AssetBase a)
  175. {
  176. Store(a, false);
  177. });
  178. }
  179. if(m_WriterThread == null)
  180. {
  181. m_WriterThread = new Thread(Writer);
  182. m_WriterThread.Start();
  183. }
  184. if (m_showStats && m_StatsThread == null)
  185. {
  186. m_StatsThread = new Thread(Stats);
  187. m_StatsThread.Start();
  188. }
  189. }
  190. m_log.Info("[FSASSETS]: FS asset service enabled");
  191. }
  192. private void Stats()
  193. {
  194. while (true)
  195. {
  196. Thread.Sleep(60000);
  197. lock (m_statsLock)
  198. {
  199. if (m_readCount > 0)
  200. {
  201. double avg = (double)m_readTicks / (double)m_readCount;
  202. // if (avg > 10000)
  203. // Environment.Exit(0);
  204. m_log.InfoFormat("[FSASSETS]: Read stats: {0} files, {1} ticks, avg {2:F2}, missing {3}, FS {4}", m_readCount, m_readTicks, (double)m_readTicks / (double)m_readCount, m_missingAssets, m_missingAssetsFS);
  205. }
  206. m_readCount = 0;
  207. m_readTicks = 0;
  208. m_missingAssets = 0;
  209. m_missingAssetsFS = 0;
  210. }
  211. }
  212. }
  213. private void Writer()
  214. {
  215. m_log.InfoFormat("[FSASSETS]: Writer started with spooldir {0} and basedir {1}", m_SpoolDirectory, m_FSBase);
  216. while (true)
  217. {
  218. string[] files = Directory.GetFiles(m_SpoolDirectory);
  219. if (files.Length > 0)
  220. {
  221. int tickCount = Environment.TickCount;
  222. for (int i = 0 ; i < files.Length ; i++)
  223. {
  224. string hash = Path.GetFileNameWithoutExtension(files[i]);
  225. string s = HashToFile(hash);
  226. string diskFile = Path.Combine(m_FSBase, s);
  227. bool pathOk = false;
  228. // The cure for chicken bones!
  229. while(true)
  230. {
  231. try
  232. {
  233. // Try to make the directory we need for this file
  234. Directory.CreateDirectory(Path.GetDirectoryName(diskFile));
  235. pathOk = true;
  236. break;
  237. }
  238. catch (System.IO.IOException)
  239. {
  240. // Creating the directory failed. This can't happen unless
  241. // a part of the path already exists as a file. Sadly the
  242. // SRAS data contains such files.
  243. string d = Path.GetDirectoryName(diskFile);
  244. // Test each path component in turn. If we can successfully
  245. // make a directory, the level below must be the chicken bone.
  246. while (d.Length > 0)
  247. {
  248. Console.WriteLine(d);
  249. try
  250. {
  251. Directory.CreateDirectory(Path.GetDirectoryName(d));
  252. }
  253. catch (System.IO.IOException)
  254. {
  255. d = Path.GetDirectoryName(d);
  256. // We failed making the directory and need to
  257. // go up a bit more
  258. continue;
  259. }
  260. // We succeeded in making the directory and (d) is
  261. // the chicken bone
  262. break;
  263. }
  264. // Is the chicken alive?
  265. if (d.Length > 0)
  266. {
  267. Console.WriteLine(d);
  268. FileAttributes attr = File.GetAttributes(d);
  269. if ((attr & FileAttributes.Directory) == 0)
  270. {
  271. // The chicken bone should be resolved.
  272. // Return to writing the file.
  273. File.Delete(d);
  274. continue;
  275. }
  276. }
  277. }
  278. // Could not resolve, skipping
  279. m_log.ErrorFormat("[FSASSETS]: Could not resolve path creation error for {0}", diskFile);
  280. break;
  281. }
  282. if (pathOk)
  283. {
  284. try
  285. {
  286. byte[] data = File.ReadAllBytes(files[i]);
  287. using (GZipStream gz = new GZipStream(new FileStream(diskFile + ".gz", FileMode.Create), CompressionMode.Compress))
  288. {
  289. gz.Write(data, 0, data.Length);
  290. }
  291. File.Delete(files[i]);
  292. //File.Move(files[i], diskFile);
  293. }
  294. catch(System.IO.IOException e)
  295. {
  296. if (e.Message.StartsWith("Win32 IO returned ERROR_ALREADY_EXISTS"))
  297. File.Delete(files[i]);
  298. else
  299. throw;
  300. }
  301. }
  302. }
  303. int totalTicks = System.Environment.TickCount - tickCount;
  304. if (totalTicks > 0) // Wrap?
  305. {
  306. m_log.InfoFormat("[FSASSETS]: Write cycle complete, {0} files, {1} ticks, avg {2:F2}", files.Length, totalTicks, (double)totalTicks / (double)files.Length);
  307. }
  308. }
  309. Thread.Sleep(1000);
  310. }
  311. }
  312. string GetSHA256Hash(byte[] data)
  313. {
  314. byte[] hash;
  315. using (SHA256 sha = SHA256.Create())
  316. hash = sha.ComputeHash(data);
  317. return BitConverter.ToString(hash).Replace("-", String.Empty);
  318. }
  319. public string HashToPath(string hash)
  320. {
  321. if (hash == null || hash.Length < 10)
  322. return "junkyard";
  323. if (m_useOsgridFormat)
  324. {
  325. /*
  326. * The code below is the OSGrid code.
  327. */
  328. return Path.Combine(hash.Substring(0, 3),
  329. Path.Combine(hash.Substring(3, 3)));
  330. }
  331. else
  332. {
  333. /*
  334. * The below is what core would normally use.
  335. * This is modified to work in OSGrid, as seen
  336. * above, because the SRAS data is structured
  337. * that way.
  338. */
  339. return Path.Combine(hash.Substring(0, 2),
  340. Path.Combine(hash.Substring(2, 2),
  341. Path.Combine(hash.Substring(4, 2),
  342. hash.Substring(6, 4))));
  343. }
  344. }
  345. private bool AssetExists(string hash)
  346. {
  347. string s = HashToFile(hash);
  348. string diskFile = Path.Combine(m_FSBase, s);
  349. if (File.Exists(diskFile + ".gz") || File.Exists(diskFile))
  350. return true;
  351. return false;
  352. }
  353. public virtual bool[] AssetsExist(string[] ids)
  354. {
  355. UUID[] uuid = Array.ConvertAll(ids, id => UUID.Parse(id));
  356. return m_DataConnector.AssetsExist(uuid);
  357. }
  358. public string HashToFile(string hash)
  359. {
  360. return Path.Combine(HashToPath(hash), hash);
  361. }
  362. public virtual AssetBase Get(string id)
  363. {
  364. string hash;
  365. return Get(id, out hash);
  366. }
  367. public AssetBase Get(string id, string ForeignAssetService, bool dummy)
  368. {
  369. return null;
  370. }
  371. private AssetBase Get(string id, out string sha)
  372. {
  373. string hash = string.Empty;
  374. int startTime = System.Environment.TickCount;
  375. AssetMetadata metadata;
  376. lock (m_readLock)
  377. {
  378. metadata = m_DataConnector.Get(id, out hash);
  379. }
  380. sha = hash;
  381. if (metadata == null)
  382. {
  383. AssetBase asset = null;
  384. if (m_FallbackService != null)
  385. {
  386. asset = m_FallbackService.Get(id);
  387. if (asset != null)
  388. {
  389. asset.Metadata.ContentType =
  390. SLUtil.SLAssetTypeToContentType((int)asset.Type);
  391. sha = GetSHA256Hash(asset.Data);
  392. m_log.InfoFormat("[FSASSETS]: Added asset {0} from fallback to local store", id);
  393. Store(asset);
  394. }
  395. }
  396. if (asset == null && m_showStats)
  397. {
  398. // m_log.InfoFormat("[FSASSETS]: Asset {0} not found", id);
  399. m_missingAssets++;
  400. }
  401. return asset;
  402. }
  403. AssetBase newAsset = new AssetBase();
  404. newAsset.Metadata = metadata;
  405. try
  406. {
  407. newAsset.Data = GetFsData(hash);
  408. if (newAsset.Data.Length == 0)
  409. {
  410. AssetBase asset = null;
  411. if (m_FallbackService != null)
  412. {
  413. asset = m_FallbackService.Get(id);
  414. if (asset != null)
  415. {
  416. asset.Metadata.ContentType =
  417. SLUtil.SLAssetTypeToContentType((int)asset.Type);
  418. sha = GetSHA256Hash(asset.Data);
  419. m_log.InfoFormat("[FSASSETS]: Added asset {0} from fallback to local store", id);
  420. Store(asset);
  421. }
  422. }
  423. if (asset == null)
  424. {
  425. if (m_showStats)
  426. m_missingAssetsFS++;
  427. // m_log.InfoFormat("[FSASSETS]: Asset {0}, hash {1} not found in FS", id, hash);
  428. }
  429. else
  430. {
  431. // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8)
  432. // Fix bad assets before sending them elsewhere
  433. if (asset.Type == (int)AssetType.Object && asset.Data != null)
  434. {
  435. string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data));
  436. asset.Data = Utils.StringToBytes(xml);
  437. }
  438. return asset;
  439. }
  440. }
  441. if (m_showStats)
  442. {
  443. lock (m_statsLock)
  444. {
  445. m_readTicks += Environment.TickCount - startTime;
  446. m_readCount++;
  447. }
  448. }
  449. // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8)
  450. // Fix bad assets before sending them elsewhere
  451. if (newAsset.Type == (int)AssetType.Object && newAsset.Data != null)
  452. {
  453. string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(newAsset.Data));
  454. newAsset.Data = Utils.StringToBytes(xml);
  455. }
  456. return newAsset;
  457. }
  458. catch (Exception exception)
  459. {
  460. m_log.Error(exception.ToString());
  461. Thread.Sleep(5000);
  462. Environment.Exit(1);
  463. return null;
  464. }
  465. }
  466. public virtual AssetMetadata GetMetadata(string id)
  467. {
  468. string hash;
  469. return m_DataConnector.Get(id, out hash);
  470. }
  471. public virtual byte[] GetData(string id)
  472. {
  473. string hash;
  474. if (m_DataConnector.Get(id, out hash) == null)
  475. return null;
  476. return GetFsData(hash);
  477. }
  478. public bool Get(string id, Object sender, AssetRetrieved handler)
  479. {
  480. AssetBase asset = Get(id);
  481. handler(id, sender, asset);
  482. return true;
  483. }
  484. public byte[] GetFsData(string hash)
  485. {
  486. string spoolFile = Path.Combine(m_SpoolDirectory, hash + ".asset");
  487. if (File.Exists(spoolFile))
  488. {
  489. try
  490. {
  491. byte[] content = File.ReadAllBytes(spoolFile);
  492. return content;
  493. }
  494. catch
  495. {
  496. }
  497. }
  498. string file = HashToFile(hash);
  499. string diskFile = Path.Combine(m_FSBase, file);
  500. if (File.Exists(diskFile + ".gz"))
  501. {
  502. try
  503. {
  504. using (GZipStream gz = new GZipStream(new FileStream(diskFile + ".gz", FileMode.Open, FileAccess.Read), CompressionMode.Decompress))
  505. {
  506. using (MemoryStream ms = new MemoryStream())
  507. {
  508. byte[] data = new byte[32768];
  509. int bytesRead;
  510. do
  511. {
  512. bytesRead = gz.Read(data, 0, 32768);
  513. if (bytesRead > 0)
  514. ms.Write(data, 0, bytesRead);
  515. } while (bytesRead > 0);
  516. return ms.ToArray();
  517. }
  518. }
  519. }
  520. catch (Exception)
  521. {
  522. return Array.Empty<byte>();
  523. }
  524. }
  525. else if (File.Exists(diskFile))
  526. {
  527. try
  528. {
  529. byte[] content = File.ReadAllBytes(diskFile);
  530. return content;
  531. }
  532. catch
  533. {
  534. }
  535. }
  536. return Array.Empty<byte>();
  537. }
  538. public virtual string Store(AssetBase asset)
  539. {
  540. return Store(asset, false);
  541. }
  542. private string Store(AssetBase asset, bool force)
  543. {
  544. int tickCount = Environment.TickCount;
  545. string hash = GetSHA256Hash(asset.Data);
  546. if (asset.Name.Length > AssetBase.MAX_ASSET_NAME)
  547. {
  548. string assetName = asset.Name.Substring(0, AssetBase.MAX_ASSET_NAME);
  549. m_log.WarnFormat(
  550. "[FSASSETS]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add",
  551. asset.Name, asset.ID, asset.Name.Length, assetName.Length);
  552. asset.Name = assetName;
  553. }
  554. if (asset.Description.Length > AssetBase.MAX_ASSET_DESC)
  555. {
  556. string assetDescription = asset.Description.Substring(0, AssetBase.MAX_ASSET_DESC);
  557. m_log.WarnFormat(
  558. "[FSASSETS]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add",
  559. asset.Description, asset.ID, asset.Description.Length, assetDescription.Length);
  560. asset.Description = assetDescription;
  561. }
  562. if (!AssetExists(hash))
  563. {
  564. string tempFile = Path.Combine(Path.Combine(m_SpoolDirectory, "spool"), hash + ".asset");
  565. string finalFile = Path.Combine(m_SpoolDirectory, hash + ".asset");
  566. if (!File.Exists(finalFile))
  567. {
  568. // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8)
  569. // Fix bad assets before storing on this server
  570. if (asset.Type == (int)AssetType.Object && asset.Data != null)
  571. {
  572. string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data));
  573. asset.Data = Utils.StringToBytes(xml);
  574. }
  575. FileStream fs = File.Create(tempFile);
  576. fs.Write(asset.Data, 0, asset.Data.Length);
  577. fs.Close();
  578. File.Move(tempFile, finalFile);
  579. }
  580. }
  581. if (asset.ID.Length == 0)
  582. {
  583. if (asset.FullID.IsZero())
  584. {
  585. asset.FullID = UUID.Random();
  586. }
  587. asset.ID = asset.FullID.ToString();
  588. }
  589. else if (asset.FullID.IsZero())
  590. {
  591. UUID uuid = UUID.Zero;
  592. if (UUID.TryParse(asset.ID, out uuid))
  593. {
  594. asset.FullID = uuid;
  595. }
  596. else
  597. {
  598. asset.FullID = UUID.Random();
  599. }
  600. }
  601. if (!m_DataConnector.Store(asset.Metadata, hash))
  602. {
  603. if (asset.Metadata.Type == -2)
  604. return asset.ID;
  605. return UUID.Zero.ToString();
  606. }
  607. else
  608. {
  609. return asset.ID;
  610. }
  611. }
  612. public bool UpdateContent(string id, byte[] data)
  613. {
  614. return false;
  615. // string oldhash;
  616. // AssetMetadata meta = m_DataConnector.Get(id, out oldhash);
  617. //
  618. // if (meta == null)
  619. // return false;
  620. //
  621. // AssetBase asset = new AssetBase();
  622. // asset.Metadata = meta;
  623. // asset.Data = data;
  624. //
  625. // Store(asset);
  626. //
  627. // return true;
  628. }
  629. public virtual bool Delete(string id)
  630. {
  631. m_DataConnector.Delete(id);
  632. return true;
  633. }
  634. private void HandleShowAssets(string module, string[] args)
  635. {
  636. int num = m_DataConnector.Count();
  637. MainConsole.Instance.Output(string.Format("Total asset count: {0}", num));
  638. }
  639. private void HandleShowDigest(string module, string[] args)
  640. {
  641. if (args.Length < 3)
  642. {
  643. MainConsole.Instance.Output("Syntax: show digest <ID>");
  644. return;
  645. }
  646. string hash;
  647. AssetBase asset = Get(args[2], out hash);
  648. if (asset == null || asset.Data.Length == 0)
  649. {
  650. MainConsole.Instance.Output("Asset not found");
  651. return;
  652. }
  653. int i;
  654. MainConsole.Instance.Output(String.Format("Name: {0}", asset.Name));
  655. MainConsole.Instance.Output(String.Format("Description: {0}", asset.Description));
  656. MainConsole.Instance.Output(String.Format("Type: {0}", asset.Type));
  657. MainConsole.Instance.Output(String.Format("Content-type: {0}", asset.Metadata.ContentType));
  658. MainConsole.Instance.Output(String.Format("Flags: {0}", asset.Metadata.Flags.ToString()));
  659. MainConsole.Instance.Output(String.Format("FS file: {0}", HashToFile(hash)));
  660. for (i = 0 ; i < 5 ; i++)
  661. {
  662. int off = i * 16;
  663. if (asset.Data.Length <= off)
  664. break;
  665. int len = 16;
  666. if (asset.Data.Length < off + len)
  667. len = asset.Data.Length - off;
  668. byte[] line = new byte[len];
  669. Array.Copy(asset.Data, off, line, 0, len);
  670. string text = BitConverter.ToString(line);
  671. MainConsole.Instance.Output(String.Format("{0:x4}: {1}", off, text));
  672. }
  673. }
  674. private void HandleDeleteAsset(string module, string[] args)
  675. {
  676. if (args.Length < 3)
  677. {
  678. MainConsole.Instance.Output("Syntax: delete asset <ID>");
  679. return;
  680. }
  681. AssetBase asset = Get(args[2]);
  682. if (asset == null || asset.Data.Length == 0)
  683. {
  684. MainConsole.Instance.Output("Asset not found");
  685. return;
  686. }
  687. m_DataConnector.Delete(args[2]);
  688. MainConsole.Instance.Output("Asset deleted");
  689. }
  690. private void HandleImportAssets(string module, string[] args)
  691. {
  692. bool force = false;
  693. if (args[0] == "force")
  694. {
  695. force = true;
  696. List<string> list = new List<string>(args);
  697. list.RemoveAt(0);
  698. args = list.ToArray();
  699. }
  700. if (args.Length < 3)
  701. {
  702. MainConsole.Instance.Output("Syntax: import <conn> <table> [<start> <count>]");
  703. }
  704. else
  705. {
  706. string conn = args[1];
  707. string table = args[2];
  708. int start = 0;
  709. int count = -1;
  710. if (args.Length > 3)
  711. {
  712. start = Convert.ToInt32(args[3]);
  713. }
  714. if (args.Length > 4)
  715. {
  716. count = Convert.ToInt32(args[4]);
  717. }
  718. m_DataConnector.Import(conn, table, start, count, force, new FSStoreDelegate(Store));
  719. }
  720. }
  721. public AssetBase GetCached(string id)
  722. {
  723. return Get(id);
  724. }
  725. public void Get(string id, string ForeignAssetService, bool StoreOnLocalGrid, SimpleAssetRetrieved callBack)
  726. {
  727. return;
  728. }
  729. }
  730. }