OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
AssetXferUploader.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.IO;
30 using System.Reflection;
31 using System.Collections.Generic;
32 using log4net;
33 using OpenMetaverse;
34 using OpenSim.Framework;
35 using OpenSim.Region.Framework.Interfaces;
36 using OpenSim.Region.Framework.Scenes;
37 using OpenSim.Services.Interfaces;
39 
40 namespace OpenSim.Region.CoreModules.Agent.AssetTransaction
41 {
42  public class AssetXferUploader
43  {
44 
45  private List<UUID> defaultIDs = new List<UUID> {
46  // Viewer's notion of the default texture
47  new UUID("5748decc-f629-461c-9a36-a35a221fe21f"), // others == default blank
48  new UUID("7ca39b4c-bd19-4699-aff7-f93fd03d3e7b"), // hair
49  new UUID("6522e74d-1660-4e7f-b601-6f48c1659a77"), // eyes
50  new UUID("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"), // skin
51  new UUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"), // transparency for alpha
52  // opensim assets textures possibly obsolete now
53  new UUID("00000000-0000-1111-9999-000000000010"),
54  new UUID("00000000-0000-1111-9999-000000000011"),
55  new UUID("00000000-0000-1111-9999-000000000012"),
56  // other transparency defined in assets
57  new UUID("3a367d1c-bef1-6d43-7595-e88c1e3aadb3"),
58  new UUID("1578a2b1-5179-4b53-b618-fe00ca5a5594"),
59  };
60 
61  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
62 
69  private enum UploadState
70  {
71  New,
72  Uploading,
73  Complete
74  }
75 
80  AgentAssetTransactions m_transactions;
81 
82  private UploadState m_uploadState = UploadState.New;
83 
84  private AssetBase m_asset;
85  private UUID InventFolder = UUID.Zero;
86  private sbyte invType = 0;
87 
88  private bool m_createItem;
89  private uint m_createItemCallback;
90 
91  private bool m_updateItem;
92  private InventoryItemBase m_updateItemData;
93 
94  private bool m_updateTaskItem;
95  private TaskInventoryItem m_updateTaskItemData;
96 
97  private string m_description = String.Empty;
98  private bool m_dumpAssetToFile;
99  private string m_name = String.Empty;
100 // private bool m_storeLocal;
101  private uint nextPerm = 0;
102  private IClientAPI ourClient;
103 
104  private UUID m_transactionID;
105 
106  private sbyte type = 0;
107  private byte wearableType = 0;
108  private byte[] m_oldData = null;
109  public ulong XferID;
110  private Scene m_Scene;
111 
126  AgentAssetTransactions transactions, Scene scene, UUID transactionID, bool dumpAssetToFile)
127  {
128  m_asset = new AssetBase();
129 
130  m_transactions = transactions;
131  m_transactionID = transactionID;
132  m_Scene = scene;
133  m_dumpAssetToFile = dumpAssetToFile;
134  }
135 
143  public bool HandleXferPacket(ulong xferID, uint packetID, byte[] data)
144  {
145 // m_log.DebugFormat(
146 // "[ASSET XFER UPLOADER]: Received packet {0} for xfer {1} (data length {2})",
147 // packetID, xferID, data.Length);
148 
149  if (XferID == xferID)
150  {
151  lock (this)
152  {
153  int assetLength = m_asset.Data.Length;
154  int dataLength = data.Length;
155 
156  if (m_asset.Data.Length > 1)
157  {
158  byte[] destinationArray = new byte[assetLength + dataLength];
159  Array.Copy(m_asset.Data, 0, destinationArray, 0, assetLength);
160  Array.Copy(data, 0, destinationArray, assetLength, dataLength);
161  m_asset.Data = destinationArray;
162  }
163  else
164  {
165  if (dataLength > 4)
166  {
167  byte[] buffer2 = new byte[dataLength - 4];
168  Array.Copy(data, 4, buffer2, 0, dataLength - 4);
169  m_asset.Data = buffer2;
170  }
171  }
172  }
173 
174  ourClient.SendConfirmXfer(xferID, packetID);
175 
176  if ((packetID & 0x80000000) != 0)
177  {
178  SendCompleteMessage();
179  return true;
180  }
181  }
182 
183  return false;
184  }
185 
199  public void StartUpload(
200  IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type, byte[] data, bool storeLocal,
201  bool tempFile)
202  {
203 // m_log.DebugFormat(
204 // "[ASSET XFER UPLOADER]: Initialised xfer from {0}, asset {1}, transaction {2}, type {3}, storeLocal {4}, tempFile {5}, already received data length {6}",
205 // remoteClient.Name, assetID, transaction, type, storeLocal, tempFile, data.Length);
206 
207  lock (this)
208  {
209  if (m_uploadState != UploadState.New)
210  {
211  m_log.WarnFormat(
212  "[ASSET XFER UPLOADER]: Tried to start upload of asset {0}, transaction {1} for {2} but this is already in state {3}. Aborting.",
213  assetID, transaction, remoteClient.Name, m_uploadState);
214 
215  return;
216  }
217 
218  m_uploadState = UploadState.Uploading;
219  }
220 
221  ourClient = remoteClient;
222 
223  m_asset.FullID = assetID;
224  m_asset.Type = type;
225  m_asset.CreatorID = remoteClient.AgentId.ToString();
226  m_asset.Data = data;
227  m_asset.Local = storeLocal;
228  m_asset.Temporary = tempFile;
229 
230 // m_storeLocal = storeLocal;
231 
232  if (m_asset.Data.Length > 2)
233  {
234  SendCompleteMessage();
235  }
236  else
237  {
238  RequestStartXfer();
239  }
240  }
241 
242  protected void RequestStartXfer()
243  {
244  XferID = Util.GetNextXferID();
245 
246 // m_log.DebugFormat(
247 // "[ASSET XFER UPLOADER]: Requesting Xfer of asset {0}, type {1}, transfer id {2} from {3}",
248 // m_asset.FullID, m_asset.Type, XferID, ourClient.Name);
249 
250  ourClient.SendXferRequest(XferID, m_asset.Type, m_asset.FullID, 0, new byte[0]);
251  }
252 
253  protected void SendCompleteMessage()
254  {
255  // We must lock in order to avoid a race with a separate thread dealing with an inventory item or create
256  // message from other client UDP.
257  lock (this)
258  {
259  m_uploadState = UploadState.Complete;
260 
261  ourClient.SendAssetUploadCompleteMessage(m_asset.Type, true, m_asset.FullID);
262 
263  if (m_createItem)
264  {
265  CompleteCreateItem(m_createItemCallback);
266  }
267  else if (m_updateItem)
268  {
269  CompleteItemUpdate(m_updateItemData);
270  }
271  else if (m_updateTaskItem)
272  {
273  CompleteTaskItemUpdate(m_updateTaskItemData);
274  }
275  else if (m_asset.Local)
276  {
277  m_Scene.AssetService.Store(m_asset);
278  }
279  }
280 
281  m_log.DebugFormat(
282  "[ASSET XFER UPLOADER]: Uploaded asset {0} for transaction {1}",
283  m_asset.FullID, m_transactionID);
284 
285  if (m_dumpAssetToFile)
286  {
287  DateTime now = DateTime.Now;
288  string filename =
289  String.Format("{6}_{7}_{0:d2}{1:d2}{2:d2}_{3:d2}{4:d2}{5:d2}.dat",
290  now.Year, now.Month, now.Day, now.Hour, now.Minute,
291  now.Second, m_asset.Name, m_asset.Type);
292  SaveAssetToFile(filename, m_asset.Data);
293  }
294  }
295 
296  private void SaveAssetToFile(string filename, byte[] data)
297  {
298  string assetPath = "UserAssets";
299  if (!Directory.Exists(assetPath))
300  {
301  Directory.CreateDirectory(assetPath);
302  }
303  FileStream fs = File.Create(Path.Combine(assetPath, filename));
304  BinaryWriter bw = new BinaryWriter(fs);
305  bw.Write(data);
306  bw.Close();
307  fs.Close();
308  }
309 
310  public void RequestCreateInventoryItem(IClientAPI remoteClient,
311  UUID folderID, uint callbackID,
312  string description, string name, sbyte invType,
313  sbyte type, byte wearableType, uint nextOwnerMask)
314  {
315  InventFolder = folderID;
316  m_name = name;
317  m_description = description;
318  this.type = type;
319  this.invType = invType;
320  this.wearableType = wearableType;
321  nextPerm = nextOwnerMask;
322  m_asset.Name = name;
323  m_asset.Description = description;
324  m_asset.Type = type;
325 
326  // We must lock to avoid a race with a separate thread uploading the asset.
327  lock (this)
328  {
329  if (m_uploadState == UploadState.Complete)
330  {
331  CompleteCreateItem(callbackID);
332  }
333  else
334  {
335  m_createItem = true; //set flag so the inventory item is created when upload is complete
336  m_createItemCallback = callbackID;
337  }
338  }
339  }
340 
342  {
343  // We must lock to avoid a race with a separate thread uploading the asset.
344  lock (this)
345  {
346  m_asset.Name = item.Name;
347  m_asset.Description = item.Description;
348  m_asset.Type = (sbyte)item.AssetType;
349 
350  // remove redundante m_Scene.InventoryService.UpdateItem
351  // if uploadState == UploadState.Complete)
352 // if (m_asset.FullID != UUID.Zero)
353 // {
354  // We must always store the item at this point even if the asset hasn't finished uploading, in order
355  // to avoid a race condition when the appearance module retrieves the item to set the asset id in
356  // the AvatarAppearance structure.
357 // item.AssetID = m_asset.FullID;
358 // m_Scene.InventoryService.UpdateItem(item);
359 // }
360 
361  if (m_uploadState == UploadState.Complete)
362  {
363  CompleteItemUpdate(item);
364  }
365  else
366  {
367  // do it here to avoid the eventual race condition
368  if (m_asset.FullID != UUID.Zero)
369  {
370  // We must always store the item at this point even if the asset hasn't finished uploading, in order
371  // to avoid a race condition when the appearance module retrieves the item to set the asset id in
372  // the AvatarAppearance structure.
373  item.AssetID = m_asset.FullID;
374  m_Scene.InventoryService.UpdateItem(item);
375  }
376 
377 
378  // m_log.DebugFormat(
379  // "[ASSET XFER UPLOADER]: Holding update inventory item request {0} for {1} pending completion of asset xfer for transaction {2}",
380  // item.Name, remoteClient.Name, transactionID);
381 
382  m_updateItem = true;
383  m_updateItemData = item;
384  }
385  }
386  }
387 
388  public void RequestUpdateTaskInventoryItem(IClientAPI remoteClient, TaskInventoryItem taskItem)
389  {
390  // We must lock to avoid a race with a separate thread uploading the asset.
391  lock (this)
392  {
393  m_asset.Name = taskItem.Name;
394  m_asset.Description = taskItem.Description;
395  m_asset.Type = (sbyte)taskItem.Type;
396  taskItem.AssetID = m_asset.FullID;
397 
398  if (m_uploadState == UploadState.Complete)
399  {
400  CompleteTaskItemUpdate(taskItem);
401  }
402  else
403  {
404  m_updateTaskItem = true;
405  m_updateTaskItemData = taskItem;
406  }
407  }
408  }
409 
414  private void CompleteItemUpdate(InventoryItemBase item)
415  {
416 // m_log.DebugFormat(
417 // "[ASSET XFER UPLOADER]: Storing asset {0} for earlier item update for {1} for {2}",
418 // m_asset.FullID, item.Name, ourClient.Name);
419 
420  ValidateAssets();
421  m_Scene.AssetService.Store(m_asset);
422  if (m_asset.FullID != UUID.Zero)
423  {
424  item.AssetID = m_asset.FullID;
425  m_Scene.InventoryService.UpdateItem(item);
426  }
427 
428  ourClient.SendInventoryItemCreateUpdate(item, m_transactionID, 0);
429 
430  m_transactions.RemoveXferUploader(m_transactionID);
431 
432  m_Scene.EventManager.TriggerOnNewInventoryItemUploadComplete(ourClient.AgentId, (AssetType)type, m_asset.FullID, m_asset.Name, 0);
433  }
434 
439  private void CompleteTaskItemUpdate(TaskInventoryItem taskItem)
440  {
441 // m_log.DebugFormat(
442 // "[ASSET XFER UPLOADER]: Storing asset {0} for earlier task item update for {1} for {2}",
443 // m_asset.FullID, taskItem.Name, ourClient.Name);
444 
445  ValidateAssets();
446  m_Scene.AssetService.Store(m_asset);
447 
448  m_transactions.RemoveXferUploader(m_transactionID);
449  }
450 
451  private void CompleteCreateItem(uint callbackID)
452  {
453  ValidateAssets();
454  m_Scene.AssetService.Store(m_asset);
455 
457  item.Owner = ourClient.AgentId;
458  item.CreatorId = ourClient.AgentId.ToString();
459  item.ID = UUID.Random();
460  item.AssetID = m_asset.FullID;
461  item.Description = m_description;
462  item.Name = m_name;
463  item.AssetType = type;
464  item.InvType = invType;
465  item.Folder = InventFolder;
466  item.BasePermissions = (uint)(PermissionMask.All | PermissionMask.Export);
467  item.CurrentPermissions = item.BasePermissions;
468  item.GroupPermissions=0;
469  item.EveryOnePermissions=0;
470  item.NextPermissions = nextPerm;
471  item.Flags = (uint) wearableType;
472  item.CreationDate = Util.UnixTimeSinceEpoch();
473 
474  m_log.DebugFormat("[XFER]: Created item {0} with asset {1}",
475  item.ID, item.AssetID);
476 
477  if (m_Scene.AddInventoryItem(item))
478  ourClient.SendInventoryItemCreateUpdate(item, m_transactionID, callbackID);
479  else
480  ourClient.SendAlertMessage("Unable to create inventory item");
481 
482  m_transactions.RemoveXferUploader(m_transactionID);
483  }
484 
485 
486  private void ValidateAssets()
487  {
488  if (m_asset.Type == (sbyte)CustomAssetType.AnimationSet)
489  {
490  AnimationSet animSet = new AnimationSet(m_asset.Data);
491 
492  bool allOk = animSet.Validate(x => {
493  int perms = m_Scene.InventoryService.GetAssetPermissions(ourClient.AgentId, x);
494  int required = (int)(PermissionMask.Transfer | PermissionMask.Copy);
495  if ((perms & required) != required)
496  return false;
497  return true;
498  });
499 
500  if (!allOk)
501  m_asset.Data = animSet.ToBytes();
502  }
503 
504  if (m_asset.Type == (sbyte)AssetType.Clothing ||
505  m_asset.Type == (sbyte)AssetType.Bodypart)
506  {
507  string content = System.Text.Encoding.ASCII.GetString(m_asset.Data);
508  string[] lines = content.Split(new char[] {'\n'});
509 
510  List<string> validated = new List<string>();
511 
512  Dictionary<int, UUID> allowed = ExtractTexturesFromOldData();
513 
514  int textures = 0;
515 
516  foreach (string line in lines)
517  {
518  try
519  {
520  if (line.StartsWith("textures "))
521  {
522  textures = Convert.ToInt32(line.Substring(9));
523  validated.Add(line);
524  }
525  else if (textures > 0)
526  {
527  string[] parts = line.Split(new char[] {' '});
528 
529  UUID tx = new UUID(parts[1]);
530  int id = Convert.ToInt32(parts[0]);
531 
532  if (defaultIDs.Contains(tx) || tx == UUID.Zero ||
533  (allowed.ContainsKey(id) && allowed[id] == tx))
534  {
535  validated.Add(parts[0] + " " + tx.ToString());
536  }
537  else
538  {
539  int perms = m_Scene.InventoryService.GetAssetPermissions(ourClient.AgentId, tx);
540  int full = (int)(PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Copy);
541 
542  if ((perms & full) != full)
543  {
544  m_log.ErrorFormat("[ASSET UPLOADER]: REJECTED update with texture {0} from {1} because they do not own the texture", tx, ourClient.AgentId);
545  validated.Add(parts[0] + " " + UUID.Zero.ToString());
546  }
547  else
548  {
549  validated.Add(line);
550  }
551  }
552  textures--;
553  }
554  else
555  {
556  validated.Add(line);
557  }
558  }
559  catch
560  {
561  // If it's malformed, skip it
562  }
563  }
564 
565  string final = String.Join("\n", validated.ToArray());
566 
567  m_asset.Data = System.Text.Encoding.ASCII.GetBytes(final);
568  }
569  }
570 
576  {
577  if (m_uploadState == UploadState.Complete)
578  {
579  ValidateAssets();
580  return m_asset;
581  }
582 
583  return null;
584  }
585 
586  public void SetOldData(byte[] d)
587  {
588  m_oldData = d;
589  }
590 
591  private Dictionary<int,UUID> ExtractTexturesFromOldData()
592  {
593  Dictionary<int,UUID> result = new Dictionary<int,UUID>();
594  if (m_oldData == null)
595  return result;
596 
597  string content = System.Text.Encoding.ASCII.GetString(m_oldData);
598  string[] lines = content.Split(new char[] {'\n'});
599 
600  int textures = 0;
601 
602  foreach (string line in lines)
603  {
604  try
605  {
606  if (line.StartsWith("textures "))
607  {
608  textures = Convert.ToInt32(line.Substring(9));
609  }
610  else if (textures > 0)
611  {
612  string[] parts = line.Split(new char[] {' '});
613 
614  UUID tx = new UUID(parts[1]);
615  int id = Convert.ToInt32(parts[0]);
616  result[id] = tx;
617  textures--;
618  }
619  }
620  catch
621  {
622  // If it's malformed, skip it
623  }
624  }
625 
626  return result;
627  }
628  }
629 }
void RequestUpdateTaskInventoryItem(IClientAPI remoteClient, TaskInventoryItem taskItem)
Represents an item in a task inventory
Asset class. All Assets are reference by this class or a class derived from this class ...
Definition: AssetBase.cs:49
void StartUpload(IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type, byte[] data, bool storeLocal, bool tempFile)
Start asset transfer from the client
void RequestUpdateInventoryItem(IClientAPI remoteClient, InventoryItemBase item)
bool HandleXferPacket(ulong xferID, uint packetID, byte[] data)
Process transfer data received from the client.
Inventory Item - contains all the properties associated with an individual inventory piece...
AssetXferUploader(AgentAssetTransactions transactions, Scene scene, UUID transactionID, bool dumpAssetToFile)
AssetXferUploader constructor
void RequestCreateInventoryItem(IClientAPI remoteClient, UUID folderID, uint callbackID, string description, string name, sbyte invType, sbyte type, byte wearableType, uint nextOwnerMask)
AssetBase GetAssetData()
Get the asset data uploaded in this transfer.
OpenSim.Framework.PermissionMask PermissionMask
OpenSim.Region.Framework.Scenes.Animation.AnimationSet AnimationSet