aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs5
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs447
2 files changed, 450 insertions, 2 deletions
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs
index 2ce5166..9a8aef0 100644
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs
+++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs
@@ -1272,8 +1272,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1272 1272
1273 if (buffer != null && buffer.Length != 0) 1273 if (buffer != null && buffer.Length != 0)
1274 { 1274 {
1275 Rest.Log.DebugFormat("{0} Entity buffer, length = {1} : <{2}>", 1275 Rest.Log.DebugFormat("{0} Entity buffer, length = {1}", MsgId, buffer.Length);
1276 MsgId, buffer.Length, encoding.GetString(buffer)); 1276 // Rest.Log.DebugFormat("{0} Entity buffer, length = {1} : <{2}>",
1277 // MsgId, buffer.Length, encoding.GetString(buffer));
1277 response.OutputStream.Write(buffer, 0, buffer.Length); 1278 response.OutputStream.Write(buffer, 0, buffer.Length);
1278 } 1279 }
1279 1280
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs
new file mode 100644
index 0000000..0acef1d
--- /dev/null
+++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs
@@ -0,0 +1,447 @@
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
28using System;
29using System.Xml;
30using System.IO;
31using OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Framework.Servers;
34
35namespace OpenSim.ApplicationPlugins.Rest.Inventory
36{
37 public class RestFileServices : IRest
38 {
39 private bool enabled = false;
40 private string qPrefix = "files";
41
42 // A simple constructor is used to handle any once-only
43 // initialization of working classes.
44
45 public RestFileServices()
46 {
47 Rest.Log.InfoFormat("{0} File services initializing", MsgId);
48 Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
49
50 // If the handler specifies a relative path for its domain
51 // then we must add the standard absolute prefix, e.g. /admin
52
53 if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
54 {
55 Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix);
56 qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
57 Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix);
58 }
59
60 // Register interface using the fully-qualified prefix
61
62 Rest.Plugin.AddPathHandler(DoFile, qPrefix, Allocate);
63
64 // Activate if all went OK
65
66 enabled = true;
67
68 Rest.Log.InfoFormat("{0} File services initialization complete", MsgId);
69 }
70
71 // Post-construction, pre-enabled initialization opportunity
72 // Not currently exploited.
73
74 public void Initialize()
75 {
76 }
77
78 // Called by the plug-in to halt REST processing. Local processing is
79 // disabled, and control blocks until all current processing has
80 // completed. No new processing will be started
81
82 public void Close()
83 {
84 enabled = false;
85 Rest.Log.InfoFormat("{0} File services ({1}) closing down", MsgId, qPrefix);
86 }
87
88 // Properties
89
90 internal string MsgId
91 {
92 get { return Rest.MsgId; }
93 }
94
95 #region Interface
96
97 private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
98 {
99 return (RequestData) new FileRequestData(request, response, prefix);
100 }
101
102 // Asset Handler
103
104 private void DoFile(RequestData rparm)
105 {
106 if (!enabled) return;
107
108 FileRequestData rdata = (FileRequestData) rparm;
109
110 Rest.Log.DebugFormat("{0} REST File handler ({1}) ENTRY", MsgId, qPrefix);
111
112 // Now that we know this is a serious attempt to
113 // access file data, we should find out who
114 // is asking, and make sure they are authorized
115 // to do so. We need to validate the caller's
116 // identity before revealing anything about the
117 // status quo. Authenticate throws an exception
118 // via Fail if no identity information is present.
119 //
120 // With the present HTTP server we can't use the
121 // builtin authentication mechanisms because they
122 // would be enforced for all in-bound requests.
123 // Instead we look at the headers ourselves and
124 // handle authentication directly.
125
126 try
127 {
128 if (!rdata.IsAuthenticated)
129 {
130 rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
131 }
132 }
133 catch (RestException e)
134 {
135 if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
136 {
137 Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
138 Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
139 rdata.request.Headers.Get("Authorization"));
140 }
141 else
142 {
143 Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
144 Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
145 rdata.request.Headers.Get("Authorization"));
146 }
147 throw (e);
148 }
149
150 // Remove the prefix and what's left are the parameters. If we don't have
151 // the parameters we need, fail the request. Parameters do NOT include
152 // any supplied query values.
153
154 if (rdata.Parameters.Length > 0)
155 {
156 switch (rdata.method)
157 {
158 case "get" :
159 DoGet(rdata);
160 break;
161 case "put" :
162 DoPut(rdata);
163 break;
164 case "post" :
165 DoPost(rdata);
166 break;
167 case "delete" :
168 DoDelete(rdata);
169 break;
170 default :
171 Rest.Log.WarnFormat("{0} File: Method not supported: {1}",
172 MsgId, rdata.method);
173 rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
174 break;
175 }
176 }
177 else
178 {
179 Rest.Log.WarnFormat("{0} File: No agent information provided", MsgId);
180 rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
181 }
182
183 Rest.Log.DebugFormat("{0} REST File handler EXIT", MsgId);
184
185 }
186
187 #endregion Interface
188
189 /// <summary>
190 /// The only parameter we recognize is a UUID.If an asset with this identification is
191 /// found, it's content, base-64 encoded, is returned to the client.
192 /// </summary>
193
194 private void DoGet(FileRequestData rdata)
195 {
196
197 string path = String.Empty;
198
199 Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
200
201 if (rdata.Parameters.Length > 1)
202 {
203 try
204 {
205 path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
206 if(File.Exists(path))
207 {
208 Rest.Log.DebugFormat("{0} File located <{1}>", MsgId, path);
209 Byte[] data = File.ReadAllBytes(path);
210 rdata.initXmlWriter();
211 rdata.writer.WriteStartElement(String.Empty,"File",String.Empty);
212 rdata.writer.WriteAttributeString("name", path);
213 rdata.writer.WriteBase64(data,0,data.Length);
214 rdata.writer.WriteFullEndElement();
215 }
216 else
217 {
218 Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, path);
219 rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0}", path));
220 }
221 }
222 catch (Exception e)
223 {
224 Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, e.Message);
225 rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
226 path, e.Message));
227 }
228 }
229
230 rdata.Complete();
231 rdata.Respond(String.Format("File <{0}> : Normal completion", rdata.method));
232
233 }
234
235 /// <summary>
236 /// UPDATE existing item, if it exists. URI identifies the item in question.
237 /// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded)
238 /// is decoded and stored in the database, identified by the supplied UUID.
239 /// </summary>
240 private void DoPut(FileRequestData rdata)
241 {
242 bool modified = false;
243 bool created = false;
244 string path = String.Empty;
245
246 Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
247
248 if (rdata.Parameters.Length > 1)
249 {
250 try
251 {
252 path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
253 bool maymod = File.Exists(path);
254
255 rdata.initXmlReader();
256 XmlReader xml = rdata.reader;
257
258 if (!xml.ReadToFollowing("File"))
259 {
260 Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
261 rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
262 }
263
264 Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
265
266 File.WriteAllBytes(path,data);
267 modified = maymod;
268 created = ! maymod;
269 }
270 catch (Exception e)
271 {
272 Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
273 e.Message);
274 }
275 }
276 else
277 {
278 Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
279 rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
280 }
281
282 if (created)
283 {
284 rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
285 rdata.Complete(Rest.HttpStatusCodeCreated);
286 }
287 else
288 {
289 if (modified)
290 {
291 rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
292 rdata.Complete(Rest.HttpStatusCodeOK);
293 }
294 else
295 {
296 rdata.Complete(Rest.HttpStatusCodeNoContent);
297 }
298 }
299
300 rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
301
302 }
303
304 /// <summary>
305 /// CREATE new item, replace if it exists. URI identifies the context for the item in question.
306 /// No parameters are required for POST, just thepayload.
307 /// </summary>
308
309 private void DoPost(FileRequestData rdata)
310 {
311
312 bool modified = false;
313 bool created = false;
314 string path = String.Empty;
315
316 Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
317
318 if (rdata.Parameters.Length > 1)
319 {
320 try
321 {
322 path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
323 bool maymod = File.Exists(path);
324
325 rdata.initXmlReader();
326 XmlReader xml = rdata.reader;
327
328 if (!xml.ReadToFollowing("File"))
329 {
330 Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
331 rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
332 }
333
334 Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
335
336 File.WriteAllBytes(path,data);
337 modified = maymod;
338 created = ! maymod;
339 }
340 catch (Exception e)
341 {
342 Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
343 e.Message);
344 }
345 }
346 else
347 {
348 Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
349 rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
350 }
351
352 if (created)
353 {
354 rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
355 rdata.Complete(Rest.HttpStatusCodeCreated);
356 }
357 else
358 {
359 if (modified)
360 {
361 rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
362 rdata.Complete(Rest.HttpStatusCodeOK);
363 }
364 else
365 {
366 rdata.Complete(Rest.HttpStatusCodeNoContent);
367 }
368 }
369
370 rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
371
372 }
373
374 /// <summary>
375 /// CREATE new item, replace if it exists. URI identifies the context for the item in question.
376 /// No parameters are required for POST, just thepayload.
377 /// </summary>
378
379 private void DoDelete(FileRequestData rdata)
380 {
381
382 bool modified = false;
383 bool created = false;
384 string path = String.Empty;
385
386 Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
387
388 if (rdata.Parameters.Length > 1)
389 {
390 try
391 {
392 path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
393
394 if(File.Exists(path))
395 {
396 File.Delete(path);
397 }
398 }
399 catch (Exception e)
400 {
401 Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
402 e.Message);
403 rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
404 path, e.Message));
405 }
406 }
407 else
408 {
409 Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
410 rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
411 }
412
413 if (created)
414 {
415 rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
416 rdata.Complete(Rest.HttpStatusCodeCreated);
417 }
418 else
419 {
420 if (modified)
421 {
422 rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
423 rdata.Complete(Rest.HttpStatusCodeOK);
424 }
425 else
426 {
427 rdata.Complete(Rest.HttpStatusCodeNoContent);
428 }
429 }
430
431 rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
432
433 }
434
435 /// <summary>
436 /// File processing has no special data area requirements.
437 /// </summary>
438
439 internal class FileRequestData : RequestData
440 {
441 internal FileRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
442 : base(request, response, prefix)
443 {
444 }
445 }
446 }
447}