diff --git a/libsecondlife/InventoryManager.cs b/libsecondlife/InventoryManager.cs
index 505dd0ec..4b8b0703 100644
--- a/libsecondlife/InventoryManager.cs
+++ b/libsecondlife/InventoryManager.cs
@@ -369,6 +369,15 @@ namespace libsecondlife
/// Filename of the task inventory asset
public delegate void TaskInventoryReplyCallback(LLUUID itemID, short serial, string assetFilename);
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public delegate void NotecardUploadedAssetCallback(bool success, string status, LLUUID itemID, LLUUID assetID);
+
#endregion Delegates
#region Events
@@ -1404,6 +1413,37 @@ namespace libsecondlife
_Client.Network.SendPacket(update);
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void RequestUploadNotecardAsset(byte[] data, LLUUID notecardID, NotecardUploadedAssetCallback callback)
+ {
+ if (_Client.Network.CurrentSim == null || _Client.Network.CurrentSim.Caps == null)
+ throw new Exception("UpdateNotecardAgentInventory capability is not currently available");
+
+ Uri url = _Client.Network.CurrentSim.Caps.CapabilityURI("UpdateNotecardAgentInventory");
+
+ if (url != null)
+ {
+ LLSDMap query = new LLSDMap();
+ query.Add("item_id", LLSD.FromUUID(notecardID));
+
+ byte[] postData = StructuredData.LLSDParser.SerializeXmlBytes(query);
+
+ // Make the request
+ CapsClient request = new CapsClient(url);
+ request.OnComplete += new CapsClient.CompleteCallback(UploadNotecardAssetResponse);
+ request.UserData = new object[2] { new KeyValuePair(callback, data), notecardID };
+ request.StartRequest(postData);
+ }
+ else
+ {
+ throw new Exception("UpdateNotecardAgentInventory capability is not currently available");
+ }
+ }
#endregion Update
#region Rez/Give
@@ -2686,6 +2726,47 @@ namespace libsecondlife
}
}
+ private void UploadNotecardAssetResponse(CapsClient client, LLSD result, Exception error)
+ {
+ LLSDMap contents = (LLSDMap)result;
+ KeyValuePair kvp = (KeyValuePair)(((object[])client.UserData)[0]);
+ NotecardUploadedAssetCallback callback = kvp.Key;
+ byte[] itemData = (byte[])kvp.Value;
+
+ string status = contents["state"].AsString();
+
+ if (status == "upload")
+ {
+ string uploadURL = contents["uploader"].AsString();
+
+ // This makes the assumption that all uploads go to CurrentSim, to avoid
+ // the problem of HttpRequestState not knowing anything about simulators
+ CapsClient upload = new CapsClient(new Uri(uploadURL));
+ upload.OnComplete += new CapsClient.CompleteCallback(UploadNotecardAssetResponse);
+ upload.UserData = new object[2] { kvp, (LLUUID)(((object[])client.UserData)[1]) };
+ upload.StartRequest(itemData, "application/octet-stream");
+ }
+ else if (status == "complete")
+ {
+ if (contents.ContainsKey("new_asset"))
+ {
+ try { callback(true, String.Empty, (LLUUID)(((object[])client.UserData)[1]), contents["new_asset"].AsUUID()); }
+ catch (Exception e) { _Client.Log(e.ToString(), Helpers.LogLevel.Error); }
+ }
+ else
+ {
+ try { callback(false, "Failed to parse asset and item UUIDs", LLUUID.Zero, LLUUID.Zero); }
+ catch (Exception e) { _Client.Log(e.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+ else
+ {
+ // Failure
+ try { callback(false, status, LLUUID.Zero, LLUUID.Zero); }
+ catch (Exception e) { _Client.Log(e.ToString(), Helpers.LogLevel.Error); }
+ }
+ }
+
#endregion Callbacks
}
}
diff --git a/libsecondlife/examples/TestClient/Commands/Inventory/CreateNotecardCommand.cs b/libsecondlife/examples/TestClient/Commands/Inventory/CreateNotecardCommand.cs
new file mode 100644
index 00000000..511d0310
--- /dev/null
+++ b/libsecondlife/examples/TestClient/Commands/Inventory/CreateNotecardCommand.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using libsecondlife;
+
+namespace libsecondlife.TestClient
+{
+ public class CreateNotecardCommand : Command
+ {
+ public CreateNotecardCommand(TestClient testClient)
+ {
+ Name = "createnotecard";
+ Description = "Creates a notecard from a local text file.";
+ }
+
+ void OnNoteUpdate(bool success, string status, LLUUID itemID, LLUUID assetID)
+ {
+ if (success)
+ Console.WriteLine("Notecard successfully uploaded, ItemID {0} AssetID {1}", itemID, assetID);
+ }
+
+ public override string Execute(string[] args, LLUUID fromAgentID)
+ {
+ if(args.Length < 1)
+ return "Usage: createnotecard filename.txt";
+
+ string file = String.Empty;
+ for (int ct = 0; ct < args.Length; ct++)
+ file = file + args[ct] + " ";
+ file = file.TrimEnd();
+
+ Console.WriteLine("Filename: {0}", file);
+ if (!File.Exists(file))
+ return String.Format("Filename '{0}' does not exist", file);
+
+ System.IO.StreamReader reader = new StreamReader(file);
+ string body = reader.ReadToEnd();
+
+ try
+ {
+ string desc = String.Format("{0} created by libsecondlife TestClient {1}", file, DateTime.Now);
+ // create the asset
+ Client.Inventory.RequestCreateItem(Client.Inventory.FindFolderForType(AssetType.Notecard), file, desc,
+ AssetType.Notecard, InventoryType.Notecard, PermissionMask.All,
+ delegate(bool success, InventoryItem item) {
+ if(success) // upload the asset
+ Client.Inventory.RequestUploadNotecardAsset(CreateNotecardAsset(body), item.UUID, new InventoryManager.NotecardUploadedAssetCallback(OnNoteUpdate));
+ }
+ );
+ return "Done";
+
+ }
+ catch (System.Exception e)
+ {
+ Client.Log(e.ToString(), Helpers.LogLevel.Error);
+ return "Error creating notecard.";
+ }
+ }
+ ///
+ ///
+ ///
+ public static byte[] CreateNotecardAsset(string body)
+ {
+ // Format the string body into Linden text
+ string lindenText = "Linden text version 1\n";
+ lindenText += "{\n";
+ lindenText += "LLEmbeddedItems version 1\n";
+ lindenText += "{\n";
+ lindenText += "count 0\n";
+ lindenText += "}\n";
+ lindenText += "Text length " + body.Length + "\n";
+ lindenText += body;
+ lindenText += "}\n";
+
+ // Assume this is a string, add 1 for the null terminator
+ byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(lindenText);
+ byte[] assetData = new byte[stringBytes.Length]; //+ 1];
+ Array.Copy(stringBytes, 0, assetData, 0, stringBytes.Length);
+
+ return assetData;
+ }
+ }
+}
\ No newline at end of file
diff --git a/libsecondlife/examples/TestClient/TestClient.csproj b/libsecondlife/examples/TestClient/TestClient.csproj
index 98456e62..df9c6053 100644
--- a/libsecondlife/examples/TestClient/TestClient.csproj
+++ b/libsecondlife/examples/TestClient/TestClient.csproj
@@ -65,6 +65,7 @@
+