corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Text;
5 using System.Threading;
6 using System.ComponentModel;
7 using System.Xml;
8 using System.Xml.Serialization;
9 using OpenMetaverse;
10 using OpenMetaverse.Packets;
11 using OpenMetaverse.TestClient;
12 using OpenMetaverse.Assets;
13  
14 namespace OpenMetaverse.TestClient
15 {
16 public class QueuedDownloadInfo
17 {
18 public UUID AssetID;
19 public UUID ItemID;
20 public UUID TaskID;
21 public UUID OwnerID;
22 public AssetType Type;
23 public string FileName;
24 public DateTime WhenRequested;
25 public bool IsRequested;
26  
27 public QueuedDownloadInfo(string file, UUID asset, UUID item, UUID task, UUID owner, AssetType type)
28 {
29 FileName = file;
30 AssetID = asset;
31 ItemID = item;
32 TaskID = task;
33 OwnerID = owner;
34 Type = type;
35 WhenRequested = DateTime.Now;
36 IsRequested = false;
37 }
38 }
39  
40 public class BackupCommand : Command
41 {
42 /// <summary>Maximum number of transfer requests to send to the server</summary>
43 private const int MAX_TRANSFERS = 10;
44  
45 // all items here, fed by the inventory walking thread
46 private Queue<QueuedDownloadInfo> PendingDownloads = new Queue<QueuedDownloadInfo>();
47  
48 // items sent to the server here
49 private List<QueuedDownloadInfo> CurrentDownloads = new List<QueuedDownloadInfo>(MAX_TRANSFERS);
50  
51 // background workers
52 private BackgroundWorker BackupWorker;
53 private BackgroundWorker QueueWorker;
54  
55 // some stats
56 private int TextItemsFound;
57 private int TextItemsTransferred;
58 private int TextItemErrors;
59  
60 #region Properties
61  
62 /// <summary>
63 /// true if either of the background threads is running
64 /// </summary>
65 private bool BackgroundBackupRunning
66 {
67 get { return InventoryWalkerRunning || QueueRunnerRunning; }
68 }
69  
70 /// <summary>
71 /// true if the thread walking inventory is running
72 /// </summary>
73 private bool InventoryWalkerRunning
74 {
75 get { return BackupWorker != null; }
76 }
77  
78 /// <summary>
79 /// true if the thread feeding the queue to the server is running
80 /// </summary>
81 private bool QueueRunnerRunning
82 {
83 get { return QueueWorker != null; }
84 }
85  
86 /// <summary>
87 /// returns a string summarizing activity
88 /// </summary>
89 /// <returns></returns>
90 private string BackgroundBackupStatus
91 {
92 get
93 {
94 StringBuilder sbResult = new StringBuilder();
95 sbResult.AppendFormat("{0} is {1} running.", Name, BoolToNot(BackgroundBackupRunning));
96 if (TextItemErrors != 0 || TextItemsFound != 0 || TextItemsTransferred != 0)
97 {
98 sbResult.AppendFormat("\r\n{0} : Inventory walker ( {1} running ) has found {2} items.",
99 Name, BoolToNot(InventoryWalkerRunning), TextItemsFound);
100 sbResult.AppendFormat("\r\n{0} : Server Transfers ( {1} running ) has transferred {2} items with {3} errors.",
101 Name, BoolToNot(QueueRunnerRunning), TextItemsTransferred, TextItemErrors);
102 sbResult.AppendFormat("\r\n{0} : {1} items in Queue, {2} items requested from server.",
103 Name, PendingDownloads.Count, CurrentDownloads.Count);
104 }
105 return sbResult.ToString();
106 }
107 }
108  
109 #endregion Properties
110  
111 public BackupCommand(TestClient testClient)
112 {
113 Name = "backuptext";
114 Description = "Backup inventory to a folder on your hard drive. Usage: " + Name + " [to <directory>] | [abort] | [status]";
115 }
116  
117 public override string Execute(string[] args, UUID fromAgentID)
118 {
119  
120 if (args.Length == 1 && args[0] == "status")
121 {
122 return BackgroundBackupStatus;
123 }
124 else if (args.Length == 1 && args[0] == "abort")
125 {
126 if (!BackgroundBackupRunning)
127 return BackgroundBackupStatus;
128  
129 BackupWorker.CancelAsync();
130 QueueWorker.CancelAsync();
131  
132 Thread.Sleep(500);
133  
134 // check status
135 return BackgroundBackupStatus;
136 }
137 else if (args.Length != 2)
138 {
139 return "Usage: " + Name + " [to <directory>] | [abort] | [status]";
140 }
141 else if (BackgroundBackupRunning)
142 {
143 return BackgroundBackupStatus;
144 }
145  
146 QueueWorker = new BackgroundWorker();
147 QueueWorker.WorkerSupportsCancellation = true;
148 QueueWorker.DoWork += new DoWorkEventHandler(bwQueueRunner_DoWork);
149 QueueWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwQueueRunner_RunWorkerCompleted);
150  
151 QueueWorker.RunWorkerAsync();
152  
153 BackupWorker = new BackgroundWorker();
154 BackupWorker.WorkerSupportsCancellation = true;
155 BackupWorker.DoWork += new DoWorkEventHandler(bwBackup_DoWork);
156 BackupWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwBackup_RunWorkerCompleted);
157  
158 BackupWorker.RunWorkerAsync(args);
159 return "Started background operations.";
160 }
161  
162 void bwQueueRunner_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
163 {
164 QueueWorker = null;
165 Console.WriteLine(BackgroundBackupStatus);
166 }
167  
168 void bwQueueRunner_DoWork(object sender, DoWorkEventArgs e)
169 {
170 TextItemErrors = TextItemsTransferred = 0;
171  
172 while (QueueWorker.CancellationPending == false)
173 {
174 // have any timed out?
175 if (CurrentDownloads.Count > 0)
176 {
177 foreach (QueuedDownloadInfo qdi in CurrentDownloads)
178 {
179 if ((qdi.WhenRequested + TimeSpan.FromSeconds(60)) < DateTime.Now)
180 {
181 Logger.DebugLog(Name + ": timeout on asset " + qdi.AssetID.ToString(), Client);
182 // submit request again
183 Client.Assets.RequestInventoryAsset(
184 qdi.AssetID, qdi.ItemID, qdi.TaskID, qdi.OwnerID, qdi.Type, true, Assets_OnAssetReceived);
185 qdi.WhenRequested = DateTime.Now;
186 qdi.IsRequested = true;
187 }
188 }
189 }
190  
191 if (PendingDownloads.Count != 0)
192 {
193 // room in the server queue?
194 if (CurrentDownloads.Count < MAX_TRANSFERS)
195 {
196 // yes
197 QueuedDownloadInfo qdi = PendingDownloads.Dequeue();
198 qdi.WhenRequested = DateTime.Now;
199 qdi.IsRequested = true;
200 Client.Assets.RequestInventoryAsset(
201 qdi.AssetID, qdi.ItemID, qdi.TaskID, qdi.OwnerID, qdi.Type, true, Assets_OnAssetReceived);
202  
203 lock (CurrentDownloads) CurrentDownloads.Add(qdi);
204 }
205 }
206  
207 if (CurrentDownloads.Count == 0 && PendingDownloads.Count == 0 && BackupWorker == null)
208 {
209 Logger.DebugLog(Name + ": both transfer queues empty AND inventory walking thread is done", Client);
210 return;
211 }
212  
213 Thread.Sleep(100);
214 }
215 }
216  
217 void bwBackup_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
218 {
219 Console.WriteLine(Name + ": Inventory walking thread done.");
220 BackupWorker = null;
221 }
222  
223 private void bwBackup_DoWork(object sender, DoWorkEventArgs e)
224 {
225 string[] args;
226  
227 TextItemsFound = 0;
228  
229 args = (string[])e.Argument;
230  
231 lock (CurrentDownloads) CurrentDownloads.Clear();
232  
233 // FIXME:
234 //Client.Inventory.RequestFolderContents(Client.Inventory.Store.RootFolder.UUID, Client.Self.AgentID,
235 // true, true, false, InventorySortOrder.ByName);
236  
237 DirectoryInfo di = new DirectoryInfo(args[1]);
238  
239 // recurse on the root folder into the entire inventory
240 BackupFolder(Client.Inventory.Store.RootNode, di.FullName);
241 }
242  
243 /// <summary>
244 /// BackupFolder - recurse through the inventory nodes sending scripts and notecards to the transfer queue
245 /// </summary>
246 /// <param name="folder">The current leaf in the inventory tree</param>
247 /// <param name="sPathSoFar">path so far, in the form @"c:\here" -- this needs to be "clean" for the current filesystem</param>
248 private void BackupFolder(InventoryNode folder, string sPathSoFar)
249 {
250  
251 // FIXME:
252 //Client.Inventory.RequestFolderContents(folder.Data.UUID, Client.Self.AgentID, true, true, false,
253 // InventorySortOrder.ByName);
254  
255 // first scan this folder for text
256 foreach (InventoryNode iNode in folder.Nodes.Values)
257 {
258 if (BackupWorker.CancellationPending)
259 return;
260 if (iNode.Data is OpenMetaverse.InventoryItem)
261 {
262 InventoryItem ii = iNode.Data as InventoryItem;
263 if (ii.AssetType == AssetType.LSLText || ii.AssetType == AssetType.Notecard)
264 {
265 // check permissions on scripts
266 if (ii.AssetType == AssetType.LSLText)
267 {
268 if ((ii.Permissions.OwnerMask & PermissionMask.Modify) == PermissionMask.None)
269 {
270 // skip this one
271 continue;
272 }
273 }
274  
275 string sExtension = (ii.AssetType == AssetType.LSLText) ? ".lsl" : ".txt";
276 // make the output file
277 string sPath = sPathSoFar + @"\" + MakeValid(ii.Name.Trim()) + sExtension;
278  
279 // create the new qdi
280 QueuedDownloadInfo qdi = new QueuedDownloadInfo(sPath, ii.AssetUUID, iNode.Data.UUID, UUID.Zero,
281 Client.Self.AgentID, ii.AssetType);
282  
283 // add it to the queue
284 lock (PendingDownloads)
285 {
286 TextItemsFound++;
287 PendingDownloads.Enqueue(qdi);
288 }
289 }
290 }
291 }
292  
293 // now run any subfolders
294 foreach (InventoryNode i in folder.Nodes.Values)
295 {
296 if (BackupWorker.CancellationPending)
297 return;
298 else if (i.Data is OpenMetaverse.InventoryFolder)
299 BackupFolder(i, sPathSoFar + @"\" + MakeValid(i.Data.Name.Trim()));
300 }
301 }
302  
303 private string MakeValid(string path)
304 {
305 // FIXME: We need to strip illegal characters out
306 return path.Trim().Replace('"', '\'');
307 }
308  
309 private void Assets_OnAssetReceived(AssetDownload asset, Asset blah)
310 {
311 lock (CurrentDownloads)
312 {
313 // see if we have this in our transfer list
314 QueuedDownloadInfo r = CurrentDownloads.Find(delegate(QueuedDownloadInfo q)
315 {
316 return q.AssetID == asset.AssetID;
317 });
318  
319 if (r != null && r.AssetID == asset.AssetID)
320 {
321 if (asset.Success)
322 {
323 // create the directory to put this in
324 Directory.CreateDirectory(Path.GetDirectoryName(r.FileName));
325  
326 // write out the file
327 File.WriteAllBytes(r.FileName, asset.AssetData);
328 Logger.DebugLog(Name + " Wrote: " + r.FileName, Client);
329 TextItemsTransferred++;
330 }
331 else
332 {
333 TextItemErrors++;
334 Console.WriteLine("{0}: Download of asset {1} ({2}) failed with status {3}", Name, r.FileName,
335 r.AssetID.ToString(), asset.Status.ToString());
336 }
337  
338 // remove the entry
339 CurrentDownloads.Remove(r);
340 }
341 }
342 }
343  
344 /// <summary>
345 /// returns blank or "not" if false
346 /// </summary>
347 /// <param name="b"></param>
348 /// <returns></returns>
349 private static string BoolToNot(bool b)
350 {
351 return b ? String.Empty : "not";
352 }
353 }
354 }