diff options
Diffstat (limited to '')
88 files changed, 2109 insertions, 9613 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 5e50903..8ff55df 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt | |||
@@ -155,6 +155,7 @@ what it is today. | |||
155 | * tglion | 155 | * tglion |
156 | * tlaukkan/Tommil (Tommi S. E. Laukkanen, Bubble Cloud) | 156 | * tlaukkan/Tommil (Tommi S. E. Laukkanen, Bubble Cloud) |
157 | * tyre | 157 | * tyre |
158 | * Vegaslon <vegaslon@gmail.com> | ||
158 | * VikingErik | 159 | * VikingErik |
159 | * Vytek | 160 | * Vytek |
160 | * webmage (IBM) | 161 | * webmage (IBM) |
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/IRest.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/IRest.cs deleted file mode 100644 index 8b43d42..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/IRest.cs +++ /dev/null | |||
@@ -1,43 +0,0 @@ | |||
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 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
29 | { | ||
30 | /// <summary> | ||
31 | /// This interface represents the boundary between the general purpose | ||
32 | /// REST plugin handling, and the functionally specific handlers. The | ||
33 | /// handler knows only to initialize and terminate all such handlers | ||
34 | /// that it finds. Implementing this interface identifies the class as | ||
35 | /// a REST handler implementation. | ||
36 | /// </summary> | ||
37 | |||
38 | internal interface IRest | ||
39 | { | ||
40 | void Initialize(); | ||
41 | void Close(); | ||
42 | } | ||
43 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/IRestHandler.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/IRestHandler.cs deleted file mode 100644 index a88fe88..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/IRestHandler.cs +++ /dev/null | |||
@@ -1,59 +0,0 @@ | |||
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 OpenSim.Framework.Servers.HttpServer; | ||
29 | |||
30 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
31 | { | ||
32 | |||
33 | /// <remarks> | ||
34 | /// The handler delegates are not noteworthy. The allocator allows | ||
35 | /// a given handler to optionally subclass the base RequestData | ||
36 | /// structure to carry any locally required per-request state | ||
37 | /// needed. | ||
38 | /// </remarks> | ||
39 | |||
40 | public delegate void RestMethodHandler(RequestData rdata); | ||
41 | public delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response, string path); | ||
42 | |||
43 | /// <summary> | ||
44 | /// This interface exports the generic plugin-handling services | ||
45 | /// available to each loaded REST services module (IRest implementation) | ||
46 | /// </summary> | ||
47 | |||
48 | internal interface IRestHandler | ||
49 | { | ||
50 | |||
51 | string MsgId { get; } | ||
52 | string RequestId { get; } | ||
53 | |||
54 | void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ma); | ||
55 | void AddStreamHandler(string httpMethod, string path, RestMethod method); | ||
56 | |||
57 | } | ||
58 | |||
59 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs deleted file mode 100644 index 10f1a6e..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs +++ /dev/null | |||
@@ -1,1465 +0,0 @@ | |||
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.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Security.Cryptography; | ||
32 | using System.Text; | ||
33 | using System.Text.RegularExpressions; | ||
34 | using System.Xml; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Servers; | ||
37 | using OpenSim.Framework.Servers.HttpServer; | ||
38 | using OpenSim.Services.Interfaces; | ||
39 | |||
40 | using OpenMetaverse; | ||
41 | |||
42 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
43 | { | ||
44 | |||
45 | /// <summary> | ||
46 | /// This class represents the current REST request. It | ||
47 | /// encapsulates the request/response state and takes care | ||
48 | /// of response generation without exposing the REST handler | ||
49 | /// to the actual mechanisms involved. | ||
50 | /// | ||
51 | /// This structure is created on entry to the Handler | ||
52 | /// method and is disposed of upon return. It is part of | ||
53 | /// the plug-in infrastructure, rather than the functionally | ||
54 | /// specific REST handler, and fundamental changes to | ||
55 | /// this should be reflected in the Rest HandlerVersion. The | ||
56 | /// object is instantiated, and may be extended by, any | ||
57 | /// given handler. See the inventory handler for an example | ||
58 | /// of this. | ||
59 | /// | ||
60 | /// If possible, the underlying request/response state is not | ||
61 | /// changed until the handler explicitly issues a Respond call. | ||
62 | /// This ensures that the request/response pair can be safely | ||
63 | /// processed by subsequent, unrelated, handlers even id the | ||
64 | /// agent handler had completed much of its processing. Think | ||
65 | /// of it as a transactional req/resp capability. | ||
66 | /// </summary> | ||
67 | |||
68 | public class RequestData | ||
69 | { | ||
70 | |||
71 | // HTTP Server interface data (Received values) | ||
72 | |||
73 | internal OSHttpRequest request = null; | ||
74 | internal OSHttpResponse response = null; | ||
75 | internal string qprefix = null; | ||
76 | |||
77 | // Request lifetime values | ||
78 | // buffer is global because it is referenced by the handler | ||
79 | // in supported of streamed requests. | ||
80 | // If a service provider wants to construct the message | ||
81 | // body explicitly it can use body to do this. The value | ||
82 | // in body is used if the buffer is still null when a response | ||
83 | // is generated. | ||
84 | // Storing information in body will suppress the return of | ||
85 | // statusBody which is only intended to report status on | ||
86 | // requests which do not themselves ordinarily generate | ||
87 | // an informational response. All of this is handled in | ||
88 | // Respond(). | ||
89 | |||
90 | internal byte[] buffer = null; | ||
91 | internal string body = null; | ||
92 | internal string bodyType = "text/html"; | ||
93 | |||
94 | // The encoding in effect is set to a server default. It may | ||
95 | // subsequently be overridden by a Content header. This | ||
96 | // value is established during construction and is used | ||
97 | // wherever encoding services are needed. | ||
98 | |||
99 | internal Encoding encoding = Rest.Encoding; | ||
100 | |||
101 | // These values are derived from the supplied URL. They | ||
102 | // are initialized during construction. | ||
103 | |||
104 | internal string path = null; | ||
105 | internal string method = null; | ||
106 | internal Uri uri = null; | ||
107 | internal string query = null; | ||
108 | internal string hostname = "localhost"; | ||
109 | internal int port = 80; | ||
110 | |||
111 | // The path part of the URI is decomposed. pathNodes | ||
112 | // is an array of every element in the URI. Parameters | ||
113 | // is an array that contains only those nodes that | ||
114 | // are not a part of the authority prefix | ||
115 | |||
116 | private string[] pathNodes = null; | ||
117 | private string[] parameters = null; | ||
118 | private static readonly string[] EmptyPath = { String.Empty }; | ||
119 | |||
120 | // The status code gets set during the course of processing | ||
121 | // and is the HTTP completion code. The status body is | ||
122 | // initialized during construction, is appended to during the | ||
123 | // course of execution, and is finalized during Respond | ||
124 | // processing. | ||
125 | // | ||
126 | // Fail processing marks the request as failed and this is | ||
127 | // then used to inhibit processing during Response processing. | ||
128 | |||
129 | internal int statusCode = 0; | ||
130 | internal string statusBody = String.Empty; | ||
131 | internal bool fail = false; | ||
132 | |||
133 | // This carries the URL to which the client should be redirected. | ||
134 | // It is set by the service provider using the Redirect call. | ||
135 | |||
136 | internal string redirectLocation = null; | ||
137 | |||
138 | // These values influence response processing. They can be set by | ||
139 | // service providers according to need. The defaults are generally | ||
140 | // good. | ||
141 | |||
142 | internal bool keepAlive = false; | ||
143 | internal bool chunked = false; | ||
144 | |||
145 | // XML related state | ||
146 | |||
147 | internal XmlWriter writer = null; | ||
148 | internal XmlReader reader = null; | ||
149 | |||
150 | // Internal working state | ||
151 | |||
152 | private StringBuilder sbuilder = new StringBuilder(1024); | ||
153 | private MemoryStream xmldata = null; | ||
154 | |||
155 | // This is used to make the response mechanism idempotent. | ||
156 | |||
157 | internal bool handled = false; | ||
158 | |||
159 | // Authentication related state | ||
160 | // | ||
161 | // Two supported authentication mechanisms are: | ||
162 | // scheme = Rest.AS_BASIC; | ||
163 | // scheme = Rest.AS_DIGEST; | ||
164 | // Presented in that order (as required by spec) | ||
165 | // A service provider can set the scheme variable to | ||
166 | // force selection of a particular authentication model | ||
167 | // (choosing from amongst those supported of course) | ||
168 | // | ||
169 | |||
170 | internal bool authenticated = false; | ||
171 | internal string scheme = Rest.Scheme; | ||
172 | internal string realm = Rest.Realm; | ||
173 | internal string domain = null; | ||
174 | internal string nonce = null; | ||
175 | internal string cnonce = null; | ||
176 | internal string qop = Rest.Qop_Auth; | ||
177 | internal string opaque = null; | ||
178 | internal string stale = null; | ||
179 | internal string algorithm = Rest.Digest_MD5; | ||
180 | internal string authParms = null; | ||
181 | internal string authPrefix = null; | ||
182 | internal string userName = String.Empty; | ||
183 | internal string userPass = String.Empty; | ||
184 | |||
185 | // Session related tables. These are only needed if QOP is set to "auth-sess" | ||
186 | // and for now at least, it is not. Session related authentication is of | ||
187 | // questionable merit in the context of REST anyway, but it is, arguably, more | ||
188 | // secure. | ||
189 | |||
190 | private static Dictionary<string,string> cntable = new Dictionary<string,string>(); | ||
191 | private static Dictionary<string,string> sktable = new Dictionary<string,string>(); | ||
192 | |||
193 | // This dictionary is used to keep track fo all of the parameters discovered | ||
194 | // when the authorisation header is anaylsed. | ||
195 | |||
196 | private Dictionary<string,string> authparms = new Dictionary<string,string>(); | ||
197 | |||
198 | // These regular expressions are used to decipher the various header entries. | ||
199 | |||
200 | private static Regex schema = new Regex("^\\s*(?<scheme>\\w+)\\s*.*", | ||
201 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | ||
202 | |||
203 | private static Regex basicParms = new Regex("^\\s*(?:\\w+)\\s+(?<pval>\\S+)\\s*", | ||
204 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | ||
205 | |||
206 | private static Regex digestParm1 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*\"(?<pval>[^\"]+)\"", | ||
207 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | ||
208 | |||
209 | private static Regex digestParm2 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*(?<pval>[^\\p{P}\\s]+)", | ||
210 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | ||
211 | |||
212 | private static Regex reuserPass = new Regex("(?<user>[^:]+):(?<pass>[\\S\\s]*)", | ||
213 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | ||
214 | |||
215 | // For efficiency, we create static instances of these objects | ||
216 | |||
217 | private static MD5 md5hash = MD5.Create(); | ||
218 | |||
219 | private static StringComparer sc = StringComparer.OrdinalIgnoreCase; | ||
220 | |||
221 | #region properties | ||
222 | |||
223 | // Just for convenience... | ||
224 | |||
225 | internal string MsgId | ||
226 | { | ||
227 | get { return Rest.MsgId; } | ||
228 | } | ||
229 | |||
230 | /// <summary> | ||
231 | /// Return a boolean indication of whether or no an authenticated user is | ||
232 | /// associated with this request. This could be wholly integrated, but | ||
233 | /// that would make authentication mandatory. | ||
234 | /// </summary> | ||
235 | |||
236 | internal bool IsAuthenticated | ||
237 | { | ||
238 | get | ||
239 | { | ||
240 | if (Rest.Authenticate) | ||
241 | { | ||
242 | if (!authenticated) | ||
243 | { | ||
244 | authenticate(); | ||
245 | } | ||
246 | |||
247 | return authenticated; | ||
248 | } | ||
249 | else return true; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /// <summary> | ||
254 | /// Access to all 'nodes' in the supplied URI as an | ||
255 | /// array of strings. | ||
256 | /// </summary> | ||
257 | |||
258 | internal string[] PathNodes | ||
259 | { | ||
260 | get | ||
261 | { | ||
262 | return pathNodes; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | /// <summary> | ||
267 | /// Access to all non-prefix 'nodes' in the supplied URI as an | ||
268 | /// array of strings. These identify a specific resource that | ||
269 | /// is managed by the authority (the prefix). | ||
270 | /// </summary> | ||
271 | |||
272 | internal string[] Parameters | ||
273 | { | ||
274 | get | ||
275 | { | ||
276 | return parameters; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | #endregion properties | ||
281 | |||
282 | #region constructors | ||
283 | |||
284 | // Constructor | ||
285 | |||
286 | internal RequestData(OSHttpRequest p_request, OSHttpResponse p_response, string p_qprefix) | ||
287 | { | ||
288 | |||
289 | request = p_request; | ||
290 | response = p_response; | ||
291 | qprefix = p_qprefix; | ||
292 | |||
293 | sbuilder.Length = 0; | ||
294 | |||
295 | encoding = request.ContentEncoding; | ||
296 | |||
297 | if (encoding == null) | ||
298 | { | ||
299 | encoding = Rest.Encoding; | ||
300 | } | ||
301 | |||
302 | method = request.HttpMethod.ToLower(); | ||
303 | initUrl(); | ||
304 | |||
305 | initParameters(p_qprefix.Length); | ||
306 | |||
307 | } | ||
308 | |||
309 | #endregion constructors | ||
310 | |||
311 | #region authentication_common | ||
312 | |||
313 | /// <summary> | ||
314 | /// The REST handler has requested authentication. Authentication | ||
315 | /// is considered to be with respect to the current values for | ||
316 | /// Realm, domain, etc. | ||
317 | /// | ||
318 | /// This method checks to see if the current request is already | ||
319 | /// authenticated for this domain. If it is, then it returns | ||
320 | /// true. If it is not, then it issues a challenge to the client | ||
321 | /// and responds negatively to the request. | ||
322 | /// | ||
323 | /// As soon as authentication failure is detected the method calls | ||
324 | /// DoChallenge() which terminates the request with REST exception | ||
325 | /// for unauthroized access. | ||
326 | /// </summary> | ||
327 | |||
328 | private void authenticate() | ||
329 | { | ||
330 | |||
331 | string authdata = request.Headers.Get("Authorization"); | ||
332 | string reqscheme = String.Empty; | ||
333 | |||
334 | // If we don't have an authorization header, then this | ||
335 | // user is certainly not authorized. This is the typical | ||
336 | // pivot for the 1st request by a client. | ||
337 | |||
338 | if (authdata == null) | ||
339 | { | ||
340 | Rest.Log.DebugFormat("{0} Challenge reason: No authorization data", MsgId); | ||
341 | DoChallenge(); | ||
342 | } | ||
343 | |||
344 | // So, we have authentication data, now we have to check to | ||
345 | // see what we got and whether or not it is valid for the | ||
346 | // current domain. To do this we need to interpret the data | ||
347 | // provided in the Authorization header. First we need to | ||
348 | // identify the scheme being used and route accordingly. | ||
349 | |||
350 | MatchCollection matches = schema.Matches(authdata); | ||
351 | |||
352 | foreach (Match m in matches) | ||
353 | { | ||
354 | Rest.Log.DebugFormat("{0} Scheme matched : {1}", MsgId, m.Groups["scheme"].Value); | ||
355 | reqscheme = m.Groups["scheme"].Value.ToLower(); | ||
356 | } | ||
357 | |||
358 | // If we want a specific authentication mechanism, make sure | ||
359 | // we get it. null indicates we don't care. non-null indicates | ||
360 | // a specific scheme requirement. | ||
361 | |||
362 | if (scheme != null && scheme.ToLower() != reqscheme) | ||
363 | { | ||
364 | Rest.Log.DebugFormat("{0} Challenge reason: Requested scheme not acceptable", MsgId); | ||
365 | DoChallenge(); | ||
366 | } | ||
367 | |||
368 | // In the future, these could be made into plug-ins... | ||
369 | // But for now at least we have no reason to use anything other | ||
370 | // then MD5. TLS/SSL are taken care of elsewhere. | ||
371 | |||
372 | switch (reqscheme) | ||
373 | { | ||
374 | case "digest" : | ||
375 | Rest.Log.DebugFormat("{0} Digest authentication offered", MsgId); | ||
376 | DoDigest(authdata); | ||
377 | break; | ||
378 | |||
379 | case "basic" : | ||
380 | Rest.Log.DebugFormat("{0} Basic authentication offered", MsgId); | ||
381 | DoBasic(authdata); | ||
382 | break; | ||
383 | } | ||
384 | |||
385 | // If the current header is invalid, then a challenge is still needed. | ||
386 | |||
387 | if (!authenticated) | ||
388 | { | ||
389 | Rest.Log.DebugFormat("{0} Challenge reason: Authentication failed", MsgId); | ||
390 | DoChallenge(); | ||
391 | } | ||
392 | |||
393 | } | ||
394 | |||
395 | /// <summary> | ||
396 | /// Construct the necessary WWW-Authenticate headers and fail the request | ||
397 | /// with a NOT AUTHORIZED response. The parameters are the union of values | ||
398 | /// required by the supported schemes. | ||
399 | /// </summary> | ||
400 | |||
401 | private void DoChallenge() | ||
402 | { | ||
403 | Flush(); | ||
404 | nonce = Rest.NonceGenerator(); // should be unique per 401 (and it is) | ||
405 | Challenge(scheme, realm, domain, nonce, opaque, stale, algorithm, qop, authParms); | ||
406 | Fail(Rest.HttpStatusCodeNotAuthorized); | ||
407 | } | ||
408 | |||
409 | /// <summary> | ||
410 | /// The Flush() call is here to support a problem encountered with the | ||
411 | /// client where an authentication rejection was lost because the rejection | ||
412 | /// may flow before the clienthas finished sending us the inbound data stream, | ||
413 | /// in which case the client responds to the socket error on out put, and | ||
414 | /// never sees the authentication challenge. The client should be fixed, | ||
415 | /// because this solution leaves the server prone to DOS attacks. A message | ||
416 | /// will be issued whenever flushing occurs. It can be enabled/disabled from | ||
417 | /// the configuration file. | ||
418 | /// </summary> | ||
419 | |||
420 | private void Flush() | ||
421 | { | ||
422 | if (Rest.FlushEnabled) | ||
423 | { | ||
424 | byte[] dbuffer = new byte[8192]; | ||
425 | Rest.Log.WarnFormat("{0} REST server is flushing the inbound data stream", MsgId); | ||
426 | while (request.InputStream.Read(dbuffer,0,dbuffer.Length) != 0); | ||
427 | } | ||
428 | return; | ||
429 | } | ||
430 | |||
431 | // Indicate that authentication is required | ||
432 | |||
433 | private void Challenge(string scheme, string realm, string domain, string nonce, | ||
434 | string opaque, string stale, string alg, | ||
435 | string qop, string auth) | ||
436 | { | ||
437 | |||
438 | sbuilder.Length = 0; | ||
439 | |||
440 | // The service provider can force a particular scheme by | ||
441 | // assigning a value to scheme. | ||
442 | |||
443 | // Basic authentication is pretty simple. | ||
444 | // Just specify the realm in question. | ||
445 | |||
446 | if (scheme == null || scheme == Rest.AS_BASIC) | ||
447 | { | ||
448 | |||
449 | sbuilder.Append(Rest.AS_BASIC); | ||
450 | |||
451 | if (realm != null) | ||
452 | { | ||
453 | sbuilder.Append(" realm="); | ||
454 | sbuilder.Append(Rest.CS_DQUOTE); | ||
455 | sbuilder.Append(realm); | ||
456 | sbuilder.Append(Rest.CS_DQUOTE); | ||
457 | } | ||
458 | AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString()); | ||
459 | } | ||
460 | |||
461 | sbuilder.Length = 0; | ||
462 | |||
463 | // Digest authentication takes somewhat more | ||
464 | // to express. | ||
465 | |||
466 | if (scheme == null || scheme == Rest.AS_DIGEST) | ||
467 | { | ||
468 | |||
469 | sbuilder.Append(Rest.AS_DIGEST); | ||
470 | sbuilder.Append(" "); | ||
471 | |||
472 | // Specify the effective realm. This should | ||
473 | // never be null if we are uthenticating, as it is required for all | ||
474 | // authentication schemes. It defines, in conjunction with the | ||
475 | // absolute URI information, the domain to which the authentication | ||
476 | // applies. It is an arbitrary string. I *believe* this allows an | ||
477 | // authentication to apply to disjoint resources within the same | ||
478 | // server. | ||
479 | |||
480 | if (realm != null) | ||
481 | { | ||
482 | sbuilder.Append("realm="); | ||
483 | sbuilder.Append(Rest.CS_DQUOTE); | ||
484 | sbuilder.Append(realm); | ||
485 | sbuilder.Append(Rest.CS_DQUOTE); | ||
486 | sbuilder.Append(Rest.CS_COMMA); | ||
487 | } | ||
488 | |||
489 | // Share our nonce. This is *uniquely* generated each time a 401 is | ||
490 | // returned. We do not generate a very sophisticated nonce at the | ||
491 | // moment (it's simply a base64 encoded UUID). | ||
492 | |||
493 | if (nonce != null) | ||
494 | { | ||
495 | sbuilder.Append("nonce="); | ||
496 | sbuilder.Append(Rest.CS_DQUOTE); | ||
497 | sbuilder.Append(nonce); | ||
498 | sbuilder.Append(Rest.CS_DQUOTE); | ||
499 | sbuilder.Append(Rest.CS_COMMA); | ||
500 | } | ||
501 | |||
502 | // The opaque string should be returned by the client unchanged in all | ||
503 | // subsequent requests. | ||
504 | |||
505 | if (opaque != null) | ||
506 | { | ||
507 | sbuilder.Append("opaque="); | ||
508 | sbuilder.Append(Rest.CS_DQUOTE); | ||
509 | sbuilder.Append(opaque); | ||
510 | sbuilder.Append(Rest.CS_DQUOTE); | ||
511 | sbuilder.Append(Rest.CS_COMMA); | ||
512 | } | ||
513 | |||
514 | // This flag indicates that the authentication was rejected because the | ||
515 | // included nonce was stale. The server might use timestamp information | ||
516 | // in the nonce to determine this. We do not. | ||
517 | |||
518 | if (stale != null) | ||
519 | { | ||
520 | sbuilder.Append("stale="); | ||
521 | sbuilder.Append(Rest.CS_DQUOTE); | ||
522 | sbuilder.Append(stale); | ||
523 | sbuilder.Append(Rest.CS_DQUOTE); | ||
524 | sbuilder.Append(Rest.CS_COMMA); | ||
525 | } | ||
526 | |||
527 | // Identifies the algorithm used to produce the digest and checksum. | ||
528 | // The default is MD5. | ||
529 | |||
530 | if (alg != null) | ||
531 | { | ||
532 | sbuilder.Append("algorithm="); | ||
533 | sbuilder.Append(alg); | ||
534 | sbuilder.Append(Rest.CS_COMMA); | ||
535 | } | ||
536 | |||
537 | // Theoretically QOP is optional, but it is required by a compliant | ||
538 | // with current versions of the scheme. In fact IE requires that QOP | ||
539 | // be specified and will refuse to authenticate otherwise. | ||
540 | |||
541 | if (qop != String.Empty) | ||
542 | { | ||
543 | sbuilder.Append("qop="); | ||
544 | sbuilder.Append(Rest.CS_DQUOTE); | ||
545 | sbuilder.Append(qop); | ||
546 | sbuilder.Append(Rest.CS_DQUOTE); | ||
547 | sbuilder.Append(Rest.CS_COMMA); | ||
548 | } | ||
549 | |||
550 | // This parameter allows for arbitrary extensions to the protocol. | ||
551 | // Unrecognized values should be simply ignored. | ||
552 | |||
553 | if (auth != null) | ||
554 | { | ||
555 | sbuilder.Append(auth); | ||
556 | sbuilder.Append(Rest.CS_COMMA); | ||
557 | } | ||
558 | |||
559 | // We don't know the userid that will be used | ||
560 | // so we cannot make any authentication domain | ||
561 | // assumptions. So the prefix will determine | ||
562 | // this. | ||
563 | |||
564 | sbuilder.Append("domain="); | ||
565 | sbuilder.Append(Rest.CS_DQUOTE); | ||
566 | sbuilder.Append(qprefix); | ||
567 | sbuilder.Append(Rest.CS_DQUOTE); | ||
568 | |||
569 | // Generate the authenticate header and we're basically | ||
570 | // done. | ||
571 | |||
572 | AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString()); | ||
573 | |||
574 | } | ||
575 | |||
576 | } | ||
577 | |||
578 | #endregion authentication_common | ||
579 | |||
580 | #region authentication_basic | ||
581 | |||
582 | /// <summary> | ||
583 | /// Interpret a BASIC authorization claim. Some clients can only | ||
584 | /// understand this and also expect it to be the first one | ||
585 | /// offered. So we do. | ||
586 | /// OpenSim also needs this, as it is the only scheme that allows | ||
587 | /// authentication using the hashed passwords stored in the | ||
588 | /// user database. | ||
589 | /// </summary> | ||
590 | |||
591 | private void DoBasic(string authdata) | ||
592 | { | ||
593 | |||
594 | string response = null; | ||
595 | |||
596 | MatchCollection matches = basicParms.Matches(authdata); | ||
597 | |||
598 | // In the case of basic authentication there is | ||
599 | // only expected to be a single argument. | ||
600 | |||
601 | foreach (Match m in matches) | ||
602 | { | ||
603 | authparms.Add("response",m.Groups["pval"].Value); | ||
604 | Rest.Log.DebugFormat("{0} Parameter matched : {1} = {2}", | ||
605 | MsgId, "response", m.Groups["pval"].Value); | ||
606 | } | ||
607 | |||
608 | // Did we get a valid response? | ||
609 | |||
610 | if (authparms.TryGetValue("response", out response)) | ||
611 | { | ||
612 | // Decode | ||
613 | response = Rest.Base64ToString(response); | ||
614 | Rest.Log.DebugFormat("{0} Auth response is: <{1}>", MsgId, response); | ||
615 | |||
616 | // Extract user & password | ||
617 | Match m = reuserPass.Match(response); | ||
618 | userName = m.Groups["user"].Value; | ||
619 | userPass = m.Groups["pass"].Value; | ||
620 | |||
621 | // Validate against user database | ||
622 | authenticated = Validate(userName,userPass); | ||
623 | } | ||
624 | |||
625 | } | ||
626 | |||
627 | /// <summary> | ||
628 | /// This method provides validation in support of the BASIC | ||
629 | /// authentication method. This is not normaly expected to be | ||
630 | /// used, but is included for completeness (and because I tried | ||
631 | /// it first). | ||
632 | /// </summary> | ||
633 | |||
634 | private bool Validate(string user, string pass) | ||
635 | { | ||
636 | |||
637 | Rest.Log.DebugFormat("{0} Simple User Validation", MsgId); | ||
638 | |||
639 | // Both values are required | ||
640 | |||
641 | if (user == null || pass == null) | ||
642 | return false; | ||
643 | |||
644 | // Eliminate any leading or trailing spaces | ||
645 | user = user.Trim(); | ||
646 | |||
647 | return vetPassword(user, pass); | ||
648 | |||
649 | } | ||
650 | |||
651 | /// <summary> | ||
652 | /// This is used by the BASIC authentication scheme to calculate | ||
653 | /// the double hash used by OpenSim to encode user's passwords. | ||
654 | /// It returns true, if the supplied password is actually correct. | ||
655 | /// If the specified user-id is not recognized, but the password | ||
656 | /// matches the God password, then this is accepted as an admin | ||
657 | /// session. | ||
658 | /// </summary> | ||
659 | |||
660 | private bool vetPassword(string user, string pass) | ||
661 | { | ||
662 | |||
663 | int x; | ||
664 | string first; | ||
665 | string last; | ||
666 | |||
667 | // Distinguish the parts, if necessary | ||
668 | |||
669 | if ((x=user.IndexOf(Rest.C_SPACE)) != -1) | ||
670 | { | ||
671 | first = user.Substring(0,x); | ||
672 | last = user.Substring(x+1); | ||
673 | } | ||
674 | else | ||
675 | { | ||
676 | first = user; | ||
677 | last = String.Empty; | ||
678 | } | ||
679 | |||
680 | UserAccount account = Rest.UserServices.GetUserAccount(UUID.Zero, first, last); | ||
681 | |||
682 | // If we don't recognize the user id, perhaps it is god? | ||
683 | if (account == null) | ||
684 | return pass == Rest.GodKey; | ||
685 | |||
686 | return (Rest.AuthServices.Authenticate(account.PrincipalID, pass, 1) != string.Empty); | ||
687 | |||
688 | } | ||
689 | |||
690 | #endregion authentication_basic | ||
691 | |||
692 | #region authentication_digest | ||
693 | |||
694 | /// <summary> | ||
695 | /// This is an RFC2617 compliant HTTP MD5 Digest authentication | ||
696 | /// implementation. It has been tested with Firefox, Java HTTP client, | ||
697 | /// and Microsoft's Internet Explorer V7. | ||
698 | /// </summary> | ||
699 | |||
700 | private void DoDigest(string authdata) | ||
701 | { | ||
702 | |||
703 | string response = null; | ||
704 | |||
705 | // Find all of the values of the for x = "y" | ||
706 | |||
707 | MatchCollection matches = digestParm1.Matches(authdata); | ||
708 | |||
709 | foreach (Match m in matches) | ||
710 | { | ||
711 | authparms.Add(m.Groups["parm"].Value,m.Groups["pval"].Value); | ||
712 | Rest.Log.DebugFormat("{0} String Parameter matched : {1} = {2}", | ||
713 | MsgId, m.Groups["parm"].Value,m.Groups["pval"].Value); | ||
714 | } | ||
715 | |||
716 | // Find all of the values of the for x = y | ||
717 | |||
718 | matches = digestParm2.Matches(authdata); | ||
719 | |||
720 | foreach (Match m in matches) | ||
721 | { | ||
722 | authparms.Add(m.Groups["parm"].Value,m.Groups["pval"].Value); | ||
723 | Rest.Log.DebugFormat("{0} Tokenized Parameter matched : {1} = {2}", | ||
724 | MsgId, m.Groups["parm"].Value,m.Groups["pval"].Value); | ||
725 | } | ||
726 | |||
727 | // A response string MUST be returned, otherwise we are | ||
728 | // NOT authenticated. | ||
729 | |||
730 | Rest.Log.DebugFormat("{0} Validating authorization parameters", MsgId); | ||
731 | |||
732 | if (authparms.TryGetValue("response", out response)) | ||
733 | { | ||
734 | |||
735 | string temp = null; | ||
736 | |||
737 | do | ||
738 | { | ||
739 | |||
740 | string nck = null; | ||
741 | string ncl = null; | ||
742 | |||
743 | // The userid is sent in clear text. Needed for the | ||
744 | // verification. | ||
745 | |||
746 | authparms.TryGetValue("username", out userName); | ||
747 | |||
748 | // All URI's of which this is a prefix are | ||
749 | // optimistically considered to be authenticated by the | ||
750 | // client. This is also needed to verify the response. | ||
751 | |||
752 | authparms.TryGetValue("uri", out authPrefix); | ||
753 | |||
754 | // There MUST be a nonce string present. We're not preserving any server | ||
755 | // side state and we can't validate the MD5 unless the client returns it | ||
756 | // to us, as it should. | ||
757 | |||
758 | if (!authparms.TryGetValue("nonce", out nonce) || nonce == null) | ||
759 | { | ||
760 | Rest.Log.WarnFormat("{0} Authentication failed: nonce missing", MsgId); | ||
761 | break; | ||
762 | } | ||
763 | |||
764 | // If there is an opaque string present, it had better | ||
765 | // match what we sent. | ||
766 | |||
767 | if (authparms.TryGetValue("opaque", out temp)) | ||
768 | { | ||
769 | if (temp != opaque) | ||
770 | { | ||
771 | Rest.Log.WarnFormat("{0} Authentication failed: bad opaque value", MsgId); | ||
772 | break; | ||
773 | } | ||
774 | } | ||
775 | |||
776 | // If an algorithm string is present, it had better | ||
777 | // match what we sent. | ||
778 | |||
779 | if (authparms.TryGetValue("algorithm", out temp)) | ||
780 | { | ||
781 | if (temp != algorithm) | ||
782 | { | ||
783 | Rest.Log.WarnFormat("{0} Authentication failed: bad algorithm value", MsgId); | ||
784 | break; | ||
785 | } | ||
786 | } | ||
787 | |||
788 | // Quality of protection considerations... | ||
789 | |||
790 | if (authparms.TryGetValue("qop", out temp)) | ||
791 | { | ||
792 | |||
793 | qop = temp.ToLower(); // replace with actual value used | ||
794 | |||
795 | // if QOP was specified then | ||
796 | // these MUST be present. | ||
797 | |||
798 | if (!authparms.ContainsKey("cnonce")) | ||
799 | { | ||
800 | Rest.Log.WarnFormat("{0} Authentication failed: cnonce missing", MsgId); | ||
801 | Fail(Rest.HttpStatusCodeBadRequest); | ||
802 | break; | ||
803 | } | ||
804 | |||
805 | cnonce = authparms["cnonce"]; | ||
806 | |||
807 | if (!authparms.TryGetValue("nc", out nck) || nck == null) | ||
808 | { | ||
809 | Rest.Log.WarnFormat("{0} Authentication failed: cnonce counter missing", MsgId); | ||
810 | Fail(Rest.HttpStatusCodeBadRequest); | ||
811 | break; | ||
812 | } | ||
813 | |||
814 | Rest.Log.DebugFormat("{0} Comparing nonce indices", MsgId); | ||
815 | |||
816 | if (cntable.TryGetValue(nonce, out ncl)) | ||
817 | { | ||
818 | Rest.Log.DebugFormat("{0} nonce values: Verify that request({1}) > Reference({2})", MsgId, nck, ncl); | ||
819 | |||
820 | if (Rest.Hex2Int(ncl) >= Rest.Hex2Int(nck)) | ||
821 | { | ||
822 | Rest.Log.WarnFormat("{0} Authentication failed: bad cnonce counter", MsgId); | ||
823 | Fail(Rest.HttpStatusCodeBadRequest); | ||
824 | break; | ||
825 | } | ||
826 | cntable[nonce] = nck; | ||
827 | } | ||
828 | else | ||
829 | { | ||
830 | lock (cntable) cntable.Add(nonce, nck); | ||
831 | } | ||
832 | |||
833 | } | ||
834 | else | ||
835 | { | ||
836 | |||
837 | qop = String.Empty; | ||
838 | |||
839 | // if QOP was not specified then | ||
840 | // these MUST NOT be present. | ||
841 | if (authparms.ContainsKey("cnonce")) | ||
842 | { | ||
843 | Rest.Log.WarnFormat("{0} Authentication failed: invalid cnonce", MsgId); | ||
844 | Fail(Rest.HttpStatusCodeBadRequest); | ||
845 | break; | ||
846 | } | ||
847 | if (authparms.ContainsKey("nc")) | ||
848 | { | ||
849 | Rest.Log.WarnFormat("{0} Authentication failed: invalid cnonce counter[2]", MsgId); | ||
850 | Fail(Rest.HttpStatusCodeBadRequest); | ||
851 | break; | ||
852 | } | ||
853 | } | ||
854 | |||
855 | // Validate the supplied userid/password info | ||
856 | |||
857 | authenticated = ValidateDigest(userName, nonce, cnonce, nck, authPrefix, response); | ||
858 | |||
859 | } | ||
860 | while (false); | ||
861 | |||
862 | } | ||
863 | else | ||
864 | Fail(Rest.HttpStatusCodeBadRequest); | ||
865 | |||
866 | } | ||
867 | |||
868 | /// <summary> | ||
869 | /// This mechanism is used by the digest authentication mechanism | ||
870 | /// to return the user's password. In fact, because the OpenSim | ||
871 | /// user's passwords are already hashed, and the HTTP mechanism | ||
872 | /// does not supply an open password, the hashed passwords cannot | ||
873 | /// be used unless the client has used the same salting mechanism | ||
874 | /// to has the password before using it in the authentication | ||
875 | /// algorithn. This is not inconceivable... | ||
876 | /// </summary> | ||
877 | |||
878 | private string getPassword(string user) | ||
879 | { | ||
880 | |||
881 | int x; | ||
882 | string first; | ||
883 | string last; | ||
884 | |||
885 | // Distinguish the parts, if necessary | ||
886 | |||
887 | if ((x=user.IndexOf(Rest.C_SPACE)) != -1) | ||
888 | { | ||
889 | first = user.Substring(0,x); | ||
890 | last = user.Substring(x+1); | ||
891 | } | ||
892 | else | ||
893 | { | ||
894 | first = user; | ||
895 | last = String.Empty; | ||
896 | } | ||
897 | |||
898 | UserAccount account = Rest.UserServices.GetUserAccount(UUID.Zero, first, last); | ||
899 | // If we don;t recognize the user id, perhaps it is god? | ||
900 | |||
901 | if (account == null) | ||
902 | { | ||
903 | Rest.Log.DebugFormat("{0} Administrator", MsgId); | ||
904 | return Rest.GodKey; | ||
905 | } | ||
906 | else | ||
907 | { | ||
908 | Rest.Log.DebugFormat("{0} Normal User {1}", MsgId, user); | ||
909 | |||
910 | // !!! REFACTORING PROBLEM | ||
911 | // This is what it was. It doesn't work in 0.7 | ||
912 | // Nothing retrieves the password from the authentication service, there's only authentication. | ||
913 | //return udata.PasswordHash; | ||
914 | return string.Empty; | ||
915 | } | ||
916 | |||
917 | } | ||
918 | |||
919 | // Validate the request-digest | ||
920 | |||
921 | private bool ValidateDigest(string user, string nonce, string cnonce, string nck, string uri, string response) | ||
922 | { | ||
923 | |||
924 | string patt = null; | ||
925 | string payl = String.Empty; | ||
926 | string KDS = null; | ||
927 | string HA1 = null; | ||
928 | string HA2 = null; | ||
929 | string pass = getPassword(user); | ||
930 | |||
931 | // Generate H(A1) | ||
932 | |||
933 | if (algorithm == Rest.Digest_MD5Sess) | ||
934 | { | ||
935 | if (!sktable.ContainsKey(cnonce)) | ||
936 | { | ||
937 | patt = String.Format("{0}:{1}:{2}:{3}:{4}", user, realm, pass, nonce, cnonce); | ||
938 | HA1 = HashToString(patt); | ||
939 | sktable.Add(cnonce, HA1); | ||
940 | } | ||
941 | else | ||
942 | { | ||
943 | HA1 = sktable[cnonce]; | ||
944 | } | ||
945 | } | ||
946 | else | ||
947 | { | ||
948 | patt = String.Format("{0}:{1}:{2}", user, realm, pass); | ||
949 | HA1 = HashToString(patt); | ||
950 | } | ||
951 | |||
952 | // Generate H(A2) | ||
953 | |||
954 | if (qop == "auth-int") | ||
955 | { | ||
956 | patt = String.Format("{0}:{1}:{2}", request.HttpMethod, uri, HashToString(payl)); | ||
957 | } | ||
958 | else | ||
959 | { | ||
960 | patt = String.Format("{0}:{1}", request.HttpMethod, uri); | ||
961 | } | ||
962 | |||
963 | HA2 = HashToString(patt); | ||
964 | |||
965 | // Generate Digest | ||
966 | |||
967 | if (qop != String.Empty) | ||
968 | { | ||
969 | patt = String.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, nonce, nck, cnonce, qop, HA2); | ||
970 | } | ||
971 | else | ||
972 | { | ||
973 | patt = String.Format("{0}:{1}:{2}", HA1, nonce, HA2); | ||
974 | } | ||
975 | |||
976 | KDS = HashToString(patt); | ||
977 | |||
978 | // Compare the generated sequence with the original | ||
979 | |||
980 | return (0 == sc.Compare(KDS, response)); | ||
981 | |||
982 | } | ||
983 | |||
984 | private string HashToString(string pattern) | ||
985 | { | ||
986 | |||
987 | Rest.Log.DebugFormat("{0} Generate <{1}>", MsgId, pattern); | ||
988 | |||
989 | byte[] hash = md5hash.ComputeHash(encoding.GetBytes(pattern)); | ||
990 | |||
991 | sbuilder.Length = 0; | ||
992 | |||
993 | for (int i = 0; i < hash.Length; i++) | ||
994 | { | ||
995 | sbuilder.Append(hash[i].ToString("x2")); | ||
996 | } | ||
997 | |||
998 | Rest.Log.DebugFormat("{0} Hash = <{1}>", MsgId, sbuilder.ToString()); | ||
999 | |||
1000 | return sbuilder.ToString(); | ||
1001 | |||
1002 | } | ||
1003 | |||
1004 | #endregion authentication_digest | ||
1005 | |||
1006 | #region service_interface | ||
1007 | |||
1008 | /// <summary> | ||
1009 | /// Conditionally set a normal completion code. This allows a normal | ||
1010 | /// execution path to default. | ||
1011 | /// </summary> | ||
1012 | |||
1013 | internal void Complete() | ||
1014 | { | ||
1015 | if (statusCode == 0) | ||
1016 | { | ||
1017 | statusCode = Rest.HttpStatusCodeOK; | ||
1018 | } | ||
1019 | } | ||
1020 | |||
1021 | /// <summary> | ||
1022 | /// Indicate a functionally-dependent conclusion to the | ||
1023 | /// request. See Rest.cs for a list of possible values. | ||
1024 | /// </summary> | ||
1025 | |||
1026 | internal void Complete(int code) | ||
1027 | { | ||
1028 | statusCode = code; | ||
1029 | } | ||
1030 | |||
1031 | /// <summary> | ||
1032 | /// Indicate that a request should be redirected, using | ||
1033 | /// the HTTP completion codes. Permanent and temporary | ||
1034 | /// redirections may be indicated. The supplied URL is | ||
1035 | /// the new location of the resource. | ||
1036 | /// </summary> | ||
1037 | |||
1038 | internal void Redirect(string Url, bool temp) | ||
1039 | { | ||
1040 | |||
1041 | redirectLocation = Url; | ||
1042 | |||
1043 | if (temp) | ||
1044 | { | ||
1045 | statusCode = Rest.HttpStatusCodeTemporaryRedirect; | ||
1046 | } | ||
1047 | else | ||
1048 | { | ||
1049 | statusCode = Rest.HttpStatusCodePermanentRedirect; | ||
1050 | } | ||
1051 | |||
1052 | Fail(statusCode, String.Empty, true); | ||
1053 | |||
1054 | } | ||
1055 | |||
1056 | /// <summary> | ||
1057 | /// Fail for an arbitrary reason. Just a failure with | ||
1058 | /// headers. The supplied message will be returned in the | ||
1059 | /// message body. | ||
1060 | /// </summary> | ||
1061 | |||
1062 | internal void Fail(int code) | ||
1063 | { | ||
1064 | Fail(code, String.Empty, false); | ||
1065 | } | ||
1066 | |||
1067 | /// <summary> | ||
1068 | /// For the more adventurous. This failure also includes a | ||
1069 | /// specified entity to be appended to the code-related | ||
1070 | /// status string. | ||
1071 | /// </summary> | ||
1072 | |||
1073 | internal void Fail(int code, string addendum) | ||
1074 | { | ||
1075 | Fail(code, addendum, false); | ||
1076 | } | ||
1077 | |||
1078 | internal void Fail(int code, string addendum, bool reset) | ||
1079 | { | ||
1080 | |||
1081 | statusCode = code; | ||
1082 | appendStatus(String.Format("({0}) : {1}", code, Rest.HttpStatusDesc[code])); | ||
1083 | |||
1084 | // Add any final addendum to the status information | ||
1085 | |||
1086 | if (addendum != String.Empty) | ||
1087 | { | ||
1088 | appendStatus(String.Format(addendum)); | ||
1089 | } | ||
1090 | |||
1091 | // Help us understand why the request is being rejected | ||
1092 | |||
1093 | if (Rest.DEBUG) | ||
1094 | { | ||
1095 | Rest.Log.DebugFormat("{0} Request Failure State Dump", MsgId); | ||
1096 | Rest.Log.DebugFormat("{0} Scheme = {1}", MsgId, scheme); | ||
1097 | Rest.Log.DebugFormat("{0} Realm = {1}", MsgId, realm); | ||
1098 | Rest.Log.DebugFormat("{0} Domain = {1}", MsgId, domain); | ||
1099 | Rest.Log.DebugFormat("{0} Nonce = {1}", MsgId, nonce); | ||
1100 | Rest.Log.DebugFormat("{0} CNonce = {1}", MsgId, cnonce); | ||
1101 | Rest.Log.DebugFormat("{0} Opaque = {1}", MsgId, opaque); | ||
1102 | Rest.Log.DebugFormat("{0} Stale = {1}", MsgId, stale); | ||
1103 | Rest.Log.DebugFormat("{0} Algorithm = {1}", MsgId, algorithm); | ||
1104 | Rest.Log.DebugFormat("{0} QOP = {1}", MsgId, qop); | ||
1105 | Rest.Log.DebugFormat("{0} AuthPrefix = {1}", MsgId, authPrefix); | ||
1106 | Rest.Log.DebugFormat("{0} UserName = {1}", MsgId, userName); | ||
1107 | Rest.Log.DebugFormat("{0} UserPass = {1}", MsgId, userPass); | ||
1108 | } | ||
1109 | |||
1110 | fail = true; | ||
1111 | |||
1112 | // Respond to the client's request, tag the response (for the | ||
1113 | // benefit of trace) to indicate the reason. | ||
1114 | |||
1115 | Respond(String.Format("Failure response: ({0}) : {1} ", | ||
1116 | code, Rest.HttpStatusDesc[code])); | ||
1117 | |||
1118 | // Finally initialize and the throw a RestException. All of the | ||
1119 | // handler's infrastructure knows that this is a "normal" | ||
1120 | // completion from a code point-of-view. | ||
1121 | |||
1122 | RestException re = new RestException(Rest.HttpStatusDesc[code]+" <"+code+">"); | ||
1123 | |||
1124 | re.statusCode = code; | ||
1125 | re.statusDesc = Rest.HttpStatusDesc[code]; | ||
1126 | re.httpmethod = method; | ||
1127 | re.httppath = path; | ||
1128 | |||
1129 | throw re; | ||
1130 | |||
1131 | } | ||
1132 | |||
1133 | // Reject this request | ||
1134 | |||
1135 | internal void Reject() | ||
1136 | { | ||
1137 | Fail(Rest.HttpStatusCodeNotImplemented, "request rejected (not implemented)"); | ||
1138 | } | ||
1139 | |||
1140 | // This MUST be called by an agent handler before it returns | ||
1141 | // control to Handle, otherwise the request will be ignored. | ||
1142 | // This is called implciitly for the REST stream handlers and | ||
1143 | // is harmless if it is called twice. | ||
1144 | |||
1145 | internal virtual bool Respond(string reason) | ||
1146 | { | ||
1147 | |||
1148 | |||
1149 | Rest.Log.DebugFormat("{0} Respond ENTRY, handled = {1}, reason = {2}", MsgId, handled, reason); | ||
1150 | |||
1151 | // We do this to try and make multiple Respond requests harmless, | ||
1152 | // as it is sometimes convenient to isse a response without | ||
1153 | // certain knowledge that it has not previously been done. | ||
1154 | |||
1155 | if (!handled) | ||
1156 | { | ||
1157 | |||
1158 | Rest.Log.DebugFormat("{0} Generating Response", MsgId); | ||
1159 | Rest.Log.DebugFormat("{0} Method is {1}", MsgId, method); | ||
1160 | |||
1161 | // A Head request can NOT have a body! So don't waste time on | ||
1162 | // formatting if we're going to reject it anyway! | ||
1163 | |||
1164 | if (method != Rest.HEAD) | ||
1165 | { | ||
1166 | |||
1167 | Rest.Log.DebugFormat("{0} Response is not abbreviated", MsgId); | ||
1168 | |||
1169 | // If the writer is non-null then we know that an XML | ||
1170 | // data component exists. Flush and close the writer and | ||
1171 | // then convert the result to the expected buffer format | ||
1172 | // unless the request has already been failed for some | ||
1173 | // reason. | ||
1174 | |||
1175 | if (writer != null) | ||
1176 | { | ||
1177 | Rest.Log.DebugFormat("{0} XML Response handler extension ENTRY", MsgId); | ||
1178 | Rest.Log.DebugFormat("{0} XML Response exists", MsgId); | ||
1179 | writer.Flush(); | ||
1180 | writer.Close(); | ||
1181 | if (!fail) | ||
1182 | { | ||
1183 | buffer = xmldata.ToArray(); | ||
1184 | AddHeader("Content-Type","application/xml"); | ||
1185 | } | ||
1186 | xmldata.Close(); | ||
1187 | Rest.Log.DebugFormat("{0} XML Response encoded", MsgId); | ||
1188 | Rest.Log.DebugFormat("{0} XML Response handler extension EXIT", MsgId); | ||
1189 | } | ||
1190 | |||
1191 | if (buffer == null && body != null) | ||
1192 | { | ||
1193 | buffer = encoding.GetBytes(body); | ||
1194 | AddHeader("Content-Type",bodyType); | ||
1195 | } | ||
1196 | |||
1197 | // OK, if the buffer contains something, regardless of how | ||
1198 | // it got there, set various response headers accordingly. | ||
1199 | |||
1200 | if (buffer != null) | ||
1201 | { | ||
1202 | Rest.Log.DebugFormat("{0} Buffer-based entity", MsgId); | ||
1203 | } | ||
1204 | else | ||
1205 | { | ||
1206 | if (statusBody != String.Empty) | ||
1207 | { | ||
1208 | statusBody += Rest.statusTail; | ||
1209 | buffer = encoding.GetBytes(statusBody); | ||
1210 | AddHeader("Content-Type","text/html"); | ||
1211 | } | ||
1212 | else | ||
1213 | { | ||
1214 | statusBody = Rest.statusHead; | ||
1215 | appendStatus(String.Format(": ({0}) {1}", | ||
1216 | statusCode, Rest.HttpStatusDesc[statusCode])); | ||
1217 | statusBody += Rest.statusTail; | ||
1218 | buffer = encoding.GetBytes(statusBody); | ||
1219 | AddHeader("Content-Type","text/html"); | ||
1220 | } | ||
1221 | } | ||
1222 | |||
1223 | response.ContentLength64 = buffer.Length; | ||
1224 | |||
1225 | if (response.ContentEncoding == null) | ||
1226 | response.ContentEncoding = encoding; | ||
1227 | |||
1228 | response.SendChunked = chunked; | ||
1229 | response.KeepAlive = keepAlive; | ||
1230 | |||
1231 | } | ||
1232 | |||
1233 | // Set the status code & description. If nothing has been stored, | ||
1234 | // we consider that a success. | ||
1235 | |||
1236 | if (statusCode == 0) | ||
1237 | { | ||
1238 | Complete(); | ||
1239 | } | ||
1240 | |||
1241 | // Set the response code in the actual carrier | ||
1242 | |||
1243 | response.StatusCode = statusCode; | ||
1244 | |||
1245 | // For a redirect we need to set the relocation header accordingly | ||
1246 | |||
1247 | if (response.StatusCode == (int) Rest.HttpStatusCodeTemporaryRedirect || | ||
1248 | response.StatusCode == (int) Rest.HttpStatusCodePermanentRedirect) | ||
1249 | { | ||
1250 | Rest.Log.DebugFormat("{0} Re-direct location is {1}", MsgId, redirectLocation); | ||
1251 | response.RedirectLocation = redirectLocation; | ||
1252 | } | ||
1253 | |||
1254 | // And include the status description if provided. | ||
1255 | |||
1256 | response.StatusDescription = Rest.HttpStatusDesc[response.StatusCode]; | ||
1257 | |||
1258 | // Finally we send back our response. | ||
1259 | |||
1260 | // We've left the setting of handled' until the | ||
1261 | // last minute because the header settings included | ||
1262 | // above are pretty harmless. But everything from | ||
1263 | // here on down probably leaves the response | ||
1264 | // element unusable by anyone else. | ||
1265 | |||
1266 | handled = true; | ||
1267 | |||
1268 | // DumpHeaders(); | ||
1269 | |||
1270 | // if (request.InputStream != null) | ||
1271 | // { | ||
1272 | // Rest.Log.DebugFormat("{0} Closing input stream", MsgId); | ||
1273 | // request.InputStream.Close(); | ||
1274 | // } | ||
1275 | |||
1276 | if (buffer != null && buffer.Length != 0) | ||
1277 | { | ||
1278 | Rest.Log.DebugFormat("{0} Entity buffer, length = {1}", MsgId, buffer.Length); | ||
1279 | // Rest.Log.DebugFormat("{0} Entity buffer, length = {1} : <{2}>", | ||
1280 | // MsgId, buffer.Length, encoding.GetString(buffer)); | ||
1281 | response.OutputStream.Write(buffer, 0, buffer.Length); | ||
1282 | } | ||
1283 | |||
1284 | // Closing the outputstream should complete the transmission process | ||
1285 | |||
1286 | Rest.Log.DebugFormat("{0} Sending response", MsgId); | ||
1287 | // response.OutputStream.Close(); | ||
1288 | response.Send(); | ||
1289 | |||
1290 | } | ||
1291 | |||
1292 | Rest.Log.DebugFormat("{0} Respond EXIT, handled = {1}, reason = {2}", MsgId, handled, reason); | ||
1293 | |||
1294 | return handled; | ||
1295 | |||
1296 | } | ||
1297 | |||
1298 | /// <summary> | ||
1299 | /// These methods allow a service provider to manipulate the | ||
1300 | /// request/response headers. The DumpHeaders method is intended | ||
1301 | /// for problem diagnosis. | ||
1302 | /// </summary> | ||
1303 | |||
1304 | internal void AddHeader(string hdr, string data) | ||
1305 | { | ||
1306 | if (Rest.DEBUG) Rest.Log.DebugFormat("{0} Adding header: <{1}: {2}>", MsgId, hdr, data); | ||
1307 | response.AddHeader(hdr, data); | ||
1308 | } | ||
1309 | |||
1310 | // internal void RemoveHeader(string hdr) | ||
1311 | // { | ||
1312 | // if (Rest.DEBUG) | ||
1313 | // { | ||
1314 | // Rest.Log.DebugFormat("{0} Removing header: <{1}>", MsgId, hdr); | ||
1315 | // if (response.Headers.Get(hdr) == null) | ||
1316 | // { | ||
1317 | // Rest.Log.DebugFormat("{0} No such header existed", | ||
1318 | // MsgId, hdr); | ||
1319 | // } | ||
1320 | // } | ||
1321 | // response.Headers.Remove(hdr); | ||
1322 | // } | ||
1323 | |||
1324 | // internal void DumpHeaders() | ||
1325 | // { | ||
1326 | // if (Rest.DEBUG) | ||
1327 | // { | ||
1328 | // for (int i=0;i<response.Headers.Count;i++) | ||
1329 | // { | ||
1330 | // Rest.Log.DebugFormat("{0} Header[{1}] : {2}", MsgId, i, | ||
1331 | // response.Headers.Get(i)); | ||
1332 | // } | ||
1333 | // } | ||
1334 | // } | ||
1335 | |||
1336 | // Setup the XML writer for output | ||
1337 | |||
1338 | internal void initXmlWriter() | ||
1339 | { | ||
1340 | XmlWriterSettings settings = new XmlWriterSettings(); | ||
1341 | xmldata = new MemoryStream(); | ||
1342 | settings.Indent = true; | ||
1343 | settings.IndentChars = " "; | ||
1344 | settings.Encoding = encoding; | ||
1345 | settings.CloseOutput = false; | ||
1346 | settings.OmitXmlDeclaration = true; | ||
1347 | settings.ConformanceLevel = ConformanceLevel.Fragment; | ||
1348 | writer = XmlWriter.Create(xmldata, settings); | ||
1349 | } | ||
1350 | |||
1351 | internal void initXmlReader() | ||
1352 | { | ||
1353 | |||
1354 | XmlReaderSettings settings = new XmlReaderSettings(); | ||
1355 | |||
1356 | settings.ConformanceLevel = ConformanceLevel.Fragment; | ||
1357 | settings.IgnoreComments = true; | ||
1358 | settings.IgnoreWhitespace = true; | ||
1359 | settings.IgnoreProcessingInstructions = true; | ||
1360 | settings.ValidationType = ValidationType.None; | ||
1361 | |||
1362 | reader = XmlReader.Create(request.InputStream,settings); | ||
1363 | |||
1364 | } | ||
1365 | |||
1366 | internal void appendStatus(string msg) | ||
1367 | { | ||
1368 | if (statusBody == String.Empty) | ||
1369 | { | ||
1370 | statusBody = String.Format(Rest.statusHead, request.HttpMethod); | ||
1371 | } | ||
1372 | |||
1373 | statusBody = String.Format("{0} {1}", statusBody, msg); | ||
1374 | } | ||
1375 | |||
1376 | #endregion service_interface | ||
1377 | |||
1378 | #region internal_methods | ||
1379 | |||
1380 | /// <summary> | ||
1381 | /// Helper methods for deconstructing and reconstructing | ||
1382 | /// URI path data. | ||
1383 | /// </summary> | ||
1384 | |||
1385 | private void initUrl() | ||
1386 | { | ||
1387 | |||
1388 | uri = request.Url; | ||
1389 | |||
1390 | if (query == null) | ||
1391 | { | ||
1392 | query = uri.Query; | ||
1393 | } | ||
1394 | |||
1395 | // If the path has not been previously initialized, | ||
1396 | // do so now. | ||
1397 | |||
1398 | if (path == null) | ||
1399 | { | ||
1400 | path = uri.AbsolutePath; | ||
1401 | if (path.EndsWith(Rest.UrlPathSeparator)) | ||
1402 | path = path.Substring(0,path.Length-1); | ||
1403 | } | ||
1404 | |||
1405 | // If we succeeded in getting a path, perform any | ||
1406 | // additional pre-processing required. | ||
1407 | |||
1408 | if (path != null) | ||
1409 | { | ||
1410 | if (Rest.ExtendedEscape) | ||
1411 | { | ||
1412 | // Handle "+". Not a standard substitution, but | ||
1413 | // common enough... | ||
1414 | path = path.Replace(Rest.C_PLUS,Rest.C_SPACE); | ||
1415 | } | ||
1416 | pathNodes = path.Split(Rest.CA_PATHSEP); | ||
1417 | } | ||
1418 | else | ||
1419 | { | ||
1420 | pathNodes = EmptyPath; | ||
1421 | } | ||
1422 | |||
1423 | // Elimiate any %-escaped values. This is left until here | ||
1424 | // so that escaped "+' are not mistakenly replaced. | ||
1425 | |||
1426 | path = Uri.UnescapeDataString(path); | ||
1427 | |||
1428 | // Request server context info | ||
1429 | |||
1430 | hostname = uri.Host; | ||
1431 | port = uri.Port; | ||
1432 | |||
1433 | } | ||
1434 | |||
1435 | private int initParameters(int prfxlen) | ||
1436 | { | ||
1437 | |||
1438 | if (prfxlen < path.Length-1) | ||
1439 | { | ||
1440 | parameters = path.Substring(prfxlen+1).Split(Rest.CA_PATHSEP); | ||
1441 | } | ||
1442 | else | ||
1443 | { | ||
1444 | parameters = new string[0]; | ||
1445 | } | ||
1446 | |||
1447 | // Generate a debug list of the decoded parameters | ||
1448 | |||
1449 | if (Rest.DEBUG && prfxlen < path.Length-1) | ||
1450 | { | ||
1451 | Rest.Log.DebugFormat("{0} URI: Parameters: {1}", MsgId, path.Substring(prfxlen)); | ||
1452 | for (int i = 0; i < parameters.Length; i++) | ||
1453 | { | ||
1454 | Rest.Log.DebugFormat("{0} Parameter[{1}]: {2}", MsgId, i, parameters[i]); | ||
1455 | } | ||
1456 | } | ||
1457 | |||
1458 | return parameters.Length; | ||
1459 | |||
1460 | } | ||
1461 | |||
1462 | #endregion internal_methods | ||
1463 | |||
1464 | } | ||
1465 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/Resources/RestHandler.addin.xml b/OpenSim/ApplicationPlugins/Rest/Inventory/Resources/RestHandler.addin.xml deleted file mode 100644 index 777a2dc..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/Resources/RestHandler.addin.xml +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | <Addin id="OpenSim.ApplicationPlugins.Rest.Inventory" version="0.1"> | ||
2 | <Runtime> | ||
3 | <Import assembly="OpenSim.ApplicationPlugins.Rest.Inventory.dll"/> | ||
4 | </Runtime> | ||
5 | <Dependencies> | ||
6 | <Addin id="OpenSim" version="0.5" /> | ||
7 | </Dependencies> | ||
8 | <Extension path = "/OpenSim/Startup"> | ||
9 | <Plugin id="RestInventory" type="OpenSim.ApplicationPlugins.Rest.Inventory.RestHandler" /> | ||
10 | </Extension> | ||
11 | </Addin> | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/Rest.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/Rest.cs deleted file mode 100644 index 9755e73..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/Rest.cs +++ /dev/null | |||
@@ -1,551 +0,0 @@ | |||
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 | |||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using System.Text; | ||
33 | using log4net; | ||
34 | using Nini.Config; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Communications; | ||
37 | using OpenSim.Services.Interfaces; | ||
38 | using IAvatarService = OpenSim.Services.Interfaces.IAvatarService; | ||
39 | |||
40 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
41 | { | ||
42 | public class Rest | ||
43 | { | ||
44 | internal static readonly ILog Log = | ||
45 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
46 | |||
47 | internal static bool DEBUG = Log.IsDebugEnabled; | ||
48 | |||
49 | /// <summary> | ||
50 | /// Supported authentication schemes | ||
51 | /// </summary> | ||
52 | |||
53 | public const string AS_BASIC = "Basic"; // simple user/password verification | ||
54 | public const string AS_DIGEST = "Digest"; // password safe authentication | ||
55 | |||
56 | /// Supported Digest algorithms | ||
57 | |||
58 | public const string Digest_MD5 = "MD5"; // assumed default if omitted | ||
59 | public const string Digest_MD5Sess = "MD5-sess"; // session-span - not good for REST? | ||
60 | |||
61 | public const string Qop_Auth = "auth"; // authentication only | ||
62 | public const string Qop_Int = "auth-int"; // TODO | ||
63 | |||
64 | /// <summary> | ||
65 | /// These values have a single value for the whole | ||
66 | /// domain and lifetime of the plugin handler. We | ||
67 | /// make them static for ease of reference within | ||
68 | /// the assembly. These are initialized by the | ||
69 | /// RestHandler class during start-up. | ||
70 | /// </summary> | ||
71 | |||
72 | internal static IRestHandler Plugin = null; | ||
73 | internal static OpenSimBase main = null; | ||
74 | internal static string Prefix = null; | ||
75 | internal static IConfig Config = null; | ||
76 | internal static string GodKey = null; | ||
77 | internal static bool Authenticate = true; | ||
78 | internal static bool Secure = true; | ||
79 | internal static bool ExtendedEscape = true; | ||
80 | internal static bool DumpAsset = false; | ||
81 | internal static bool Fill = true; | ||
82 | internal static bool FlushEnabled = true; | ||
83 | internal static string Realm = "OpenSim REST"; | ||
84 | internal static string Scheme = AS_BASIC; | ||
85 | internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4 | ||
86 | |||
87 | /// <summary> | ||
88 | /// These are all dependent upon the Comms manager | ||
89 | /// being initialized. So they have to be properties | ||
90 | /// because the comms manager is now a module and is | ||
91 | /// not guaranteed to be there when the rest handler | ||
92 | /// initializes. | ||
93 | /// </summary> | ||
94 | |||
95 | internal static IInventoryService InventoryServices | ||
96 | { | ||
97 | get { return main.SceneManager.CurrentOrFirstScene.InventoryService; } | ||
98 | } | ||
99 | |||
100 | internal static IUserAccountService UserServices | ||
101 | { | ||
102 | get { return main.SceneManager.CurrentOrFirstScene.UserAccountService; } | ||
103 | } | ||
104 | |||
105 | internal static IAuthenticationService AuthServices | ||
106 | { | ||
107 | get { return main.SceneManager.CurrentOrFirstScene.AuthenticationService; } | ||
108 | } | ||
109 | |||
110 | internal static IAvatarService AvatarServices | ||
111 | { | ||
112 | get { return main.SceneManager.CurrentOrFirstScene.AvatarService; } | ||
113 | } | ||
114 | |||
115 | internal static IAssetService AssetServices | ||
116 | { | ||
117 | get { return main.SceneManager.CurrentOrFirstScene.AssetService; } | ||
118 | } | ||
119 | |||
120 | /// <summary> | ||
121 | /// HTTP requires that status information be generated for PUT | ||
122 | /// and POST opertaions. This is in support of that. The | ||
123 | /// operation verb gets substituted into the first string, | ||
124 | /// and the completion code is inserted into the tail. The | ||
125 | /// strings are put here to encourage consistency. | ||
126 | /// </summary> | ||
127 | |||
128 | internal static string statusHead = "<html><body><title>{0} status</title><break>"; | ||
129 | internal static string statusTail = "</body></html>"; | ||
130 | |||
131 | internal static Dictionary<int,string> HttpStatusDesc; | ||
132 | |||
133 | static Rest() | ||
134 | { | ||
135 | HttpStatusDesc = new Dictionary<int,string>(); | ||
136 | if (HttpStatusCodeArray.Length != HttpStatusDescArray.Length) | ||
137 | { | ||
138 | Log.ErrorFormat("{0} HTTP Status Code and Description arrays do not match"); | ||
139 | throw new Exception("HTTP Status array discrepancy"); | ||
140 | } | ||
141 | |||
142 | // Repackage the data into something more tractable. The sparse | ||
143 | // nature of HTTP return codes makes an array a bad choice. | ||
144 | |||
145 | for (int i=0; i<HttpStatusCodeArray.Length; i++) | ||
146 | { | ||
147 | HttpStatusDesc.Add(HttpStatusCodeArray[i], HttpStatusDescArray[i]); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | internal static int CreationDate | ||
152 | { | ||
153 | get { return (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; } | ||
154 | } | ||
155 | |||
156 | internal static string MsgId | ||
157 | { | ||
158 | get { return Plugin.MsgId; } | ||
159 | } | ||
160 | |||
161 | internal static string RequestId | ||
162 | { | ||
163 | get { return Plugin.RequestId; } | ||
164 | } | ||
165 | |||
166 | internal static Encoding Encoding = Util.UTF8; | ||
167 | |||
168 | /// <summary> | ||
169 | /// Version control for REST implementation. This | ||
170 | /// refers to the overall infrastructure represented | ||
171 | /// by the following classes | ||
172 | /// RequestData | ||
173 | /// RequestInventoryPlugin | ||
174 | /// Rest | ||
175 | /// It does no describe implementation classes such as | ||
176 | /// RestInventoryServices, which may morph much more | ||
177 | /// often. Such classes ARE dependent upon this however | ||
178 | /// and should check it in their Initialize method. | ||
179 | /// </summary> | ||
180 | |||
181 | public static readonly float Version = 1.0F; | ||
182 | public const string Name = "REST 1.0"; | ||
183 | |||
184 | /// <summary> | ||
185 | /// Currently defined HTTP methods. | ||
186 | /// Only GET and HEAD are required to be | ||
187 | /// supported by all servers. See Respond | ||
188 | /// to see how these are handled. | ||
189 | /// </summary> | ||
190 | |||
191 | // REST AGENT 1.0 interpretations | ||
192 | public const string GET = "get"; // information retrieval - server state unchanged | ||
193 | public const string HEAD = "head"; // same as get except only the headers are returned. | ||
194 | public const string POST = "post"; // Replace the URI designated resource with the entity. | ||
195 | public const string PUT = "put"; // Add the entity to the context represented by the URI | ||
196 | public const string DELETE = "delete"; // Remove the URI designated resource from the server. | ||
197 | |||
198 | public const string OPTIONS = "options"; // | ||
199 | public const string TRACE = "trace"; // | ||
200 | public const string CONNECT = "connect"; // | ||
201 | |||
202 | // Define this in one place... | ||
203 | |||
204 | public const string UrlPathSeparator = "/"; | ||
205 | public const string UrlMethodSeparator = ":"; | ||
206 | |||
207 | // Redirection qualifications | ||
208 | |||
209 | public const bool PERMANENT = false; | ||
210 | public const bool TEMPORARY = true; | ||
211 | |||
212 | // Constant arrays used by String.Split | ||
213 | |||
214 | public static readonly char C_SPACE = ' '; | ||
215 | public static readonly char C_SLASH = '/'; | ||
216 | public static readonly char C_PATHSEP = '/'; | ||
217 | public static readonly char C_COLON = ':'; | ||
218 | public static readonly char C_PLUS = '+'; | ||
219 | public static readonly char C_PERIOD = '.'; | ||
220 | public static readonly char C_COMMA = ','; | ||
221 | public static readonly char C_DQUOTE = '"'; | ||
222 | |||
223 | public static readonly string CS_SPACE = " "; | ||
224 | public static readonly string CS_SLASH = "/"; | ||
225 | public static readonly string CS_PATHSEP = "/"; | ||
226 | public static readonly string CS_COLON = ":"; | ||
227 | public static readonly string CS_PLUS = "+"; | ||
228 | public static readonly string CS_PERIOD = "."; | ||
229 | public static readonly string CS_COMMA = ","; | ||
230 | public static readonly string CS_DQUOTE = "\""; | ||
231 | |||
232 | public static readonly char[] CA_SPACE = { C_SPACE }; | ||
233 | public static readonly char[] CA_SLASH = { C_SLASH }; | ||
234 | public static readonly char[] CA_PATHSEP = { C_PATHSEP }; | ||
235 | public static readonly char[] CA_COLON = { C_COLON }; | ||
236 | public static readonly char[] CA_PERIOD = { C_PERIOD }; | ||
237 | public static readonly char[] CA_PLUS = { C_PLUS }; | ||
238 | public static readonly char[] CA_COMMA = { C_COMMA }; | ||
239 | public static readonly char[] CA_DQUOTE = { C_DQUOTE }; | ||
240 | |||
241 | // HTTP Code Values (in value order) | ||
242 | |||
243 | public const int HttpStatusCodeContinue = 100; | ||
244 | public const int HttpStatusCodeSwitchingProtocols = 101; | ||
245 | |||
246 | public const int HttpStatusCodeOK = 200; | ||
247 | public const int HttpStatusCodeCreated = 201; | ||
248 | public const int HttpStatusCodeAccepted = 202; | ||
249 | public const int HttpStatusCodeNonAuthoritative = 203; | ||
250 | public const int HttpStatusCodeNoContent = 204; | ||
251 | public const int HttpStatusCodeResetContent = 205; | ||
252 | public const int HttpStatusCodePartialContent = 206; | ||
253 | |||
254 | public const int HttpStatusCodeMultipleChoices = 300; | ||
255 | public const int HttpStatusCodePermanentRedirect = 301; | ||
256 | public const int HttpStatusCodeFound = 302; | ||
257 | public const int HttpStatusCodeSeeOther = 303; | ||
258 | public const int HttpStatusCodeNotModified = 304; | ||
259 | public const int HttpStatusCodeUseProxy = 305; | ||
260 | public const int HttpStatusCodeReserved306 = 306; | ||
261 | public const int HttpStatusCodeTemporaryRedirect = 307; | ||
262 | |||
263 | public const int HttpStatusCodeBadRequest = 400; | ||
264 | public const int HttpStatusCodeNotAuthorized = 401; | ||
265 | public const int HttpStatusCodePaymentRequired = 402; | ||
266 | public const int HttpStatusCodeForbidden = 403; | ||
267 | public const int HttpStatusCodeNotFound = 404; | ||
268 | public const int HttpStatusCodeMethodNotAllowed = 405; | ||
269 | public const int HttpStatusCodeNotAcceptable = 406; | ||
270 | public const int HttpStatusCodeProxyAuthenticate = 407; | ||
271 | public const int HttpStatusCodeTimeOut = 408; | ||
272 | public const int HttpStatusCodeConflict = 409; | ||
273 | public const int HttpStatusCodeGone = 410; | ||
274 | public const int HttpStatusCodeLengthRequired = 411; | ||
275 | public const int HttpStatusCodePreconditionFailed = 412; | ||
276 | public const int HttpStatusCodeEntityTooLarge = 413; | ||
277 | public const int HttpStatusCodeUriTooLarge = 414; | ||
278 | public const int HttpStatusCodeUnsupportedMedia = 415; | ||
279 | public const int HttpStatusCodeRangeNotSatsified = 416; | ||
280 | public const int HttpStatusCodeExpectationFailed = 417; | ||
281 | |||
282 | public const int HttpStatusCodeServerError = 500; | ||
283 | public const int HttpStatusCodeNotImplemented = 501; | ||
284 | public const int HttpStatusCodeBadGateway = 502; | ||
285 | public const int HttpStatusCodeServiceUnavailable = 503; | ||
286 | public const int HttpStatusCodeGatewayTimeout = 504; | ||
287 | public const int HttpStatusCodeHttpVersionError = 505; | ||
288 | |||
289 | public static readonly int[] HttpStatusCodeArray = { | ||
290 | HttpStatusCodeContinue, | ||
291 | HttpStatusCodeSwitchingProtocols, | ||
292 | HttpStatusCodeOK, | ||
293 | HttpStatusCodeCreated, | ||
294 | HttpStatusCodeAccepted, | ||
295 | HttpStatusCodeNonAuthoritative, | ||
296 | HttpStatusCodeNoContent, | ||
297 | HttpStatusCodeResetContent, | ||
298 | HttpStatusCodePartialContent, | ||
299 | HttpStatusCodeMultipleChoices, | ||
300 | HttpStatusCodePermanentRedirect, | ||
301 | HttpStatusCodeFound, | ||
302 | HttpStatusCodeSeeOther, | ||
303 | HttpStatusCodeNotModified, | ||
304 | HttpStatusCodeUseProxy, | ||
305 | HttpStatusCodeReserved306, | ||
306 | HttpStatusCodeTemporaryRedirect, | ||
307 | HttpStatusCodeBadRequest, | ||
308 | HttpStatusCodeNotAuthorized, | ||
309 | HttpStatusCodePaymentRequired, | ||
310 | HttpStatusCodeForbidden, | ||
311 | HttpStatusCodeNotFound, | ||
312 | HttpStatusCodeMethodNotAllowed, | ||
313 | HttpStatusCodeNotAcceptable, | ||
314 | HttpStatusCodeProxyAuthenticate, | ||
315 | HttpStatusCodeTimeOut, | ||
316 | HttpStatusCodeConflict, | ||
317 | HttpStatusCodeGone, | ||
318 | HttpStatusCodeLengthRequired, | ||
319 | HttpStatusCodePreconditionFailed, | ||
320 | HttpStatusCodeEntityTooLarge, | ||
321 | HttpStatusCodeUriTooLarge, | ||
322 | HttpStatusCodeUnsupportedMedia, | ||
323 | HttpStatusCodeRangeNotSatsified, | ||
324 | HttpStatusCodeExpectationFailed, | ||
325 | HttpStatusCodeServerError, | ||
326 | HttpStatusCodeNotImplemented, | ||
327 | HttpStatusCodeBadGateway, | ||
328 | HttpStatusCodeServiceUnavailable, | ||
329 | HttpStatusCodeGatewayTimeout, | ||
330 | HttpStatusCodeHttpVersionError | ||
331 | }; | ||
332 | |||
333 | // HTTP Status Descriptions (in status code order) | ||
334 | // This array must be kept strictly consistent with respect | ||
335 | // to the status code array above. | ||
336 | |||
337 | public static readonly string[] HttpStatusDescArray = { | ||
338 | "Continue Request", | ||
339 | "Switching Protocols", | ||
340 | "OK", | ||
341 | "CREATED", | ||
342 | "ACCEPTED", | ||
343 | "NON-AUTHORITATIVE INFORMATION", | ||
344 | "NO CONTENT", | ||
345 | "RESET CONTENT", | ||
346 | "PARTIAL CONTENT", | ||
347 | "MULTIPLE CHOICES", | ||
348 | "PERMANENT REDIRECT", | ||
349 | "FOUND", | ||
350 | "SEE OTHER", | ||
351 | "NOT MODIFIED", | ||
352 | "USE PROXY", | ||
353 | "RESERVED CODE 306", | ||
354 | "TEMPORARY REDIRECT", | ||
355 | "BAD REQUEST", | ||
356 | "NOT AUTHORIZED", | ||
357 | "PAYMENT REQUIRED", | ||
358 | "FORBIDDEN", | ||
359 | "NOT FOUND", | ||
360 | "METHOD NOT ALLOWED", | ||
361 | "NOT ACCEPTABLE", | ||
362 | "PROXY AUTHENTICATION REQUIRED", | ||
363 | "TIMEOUT", | ||
364 | "CONFLICT", | ||
365 | "GONE", | ||
366 | "LENGTH REQUIRED", | ||
367 | "PRECONDITION FAILED", | ||
368 | "ENTITY TOO LARGE", | ||
369 | "URI TOO LARGE", | ||
370 | "UNSUPPORTED MEDIA", | ||
371 | "RANGE NOT SATISFIED", | ||
372 | "EXPECTATION FAILED", | ||
373 | "SERVER ERROR", | ||
374 | "NOT IMPLEMENTED", | ||
375 | "BAD GATEWAY", | ||
376 | "SERVICE UNAVAILABLE", | ||
377 | "GATEWAY TIMEOUT", | ||
378 | "HTTP VERSION NOT SUPPORTED" | ||
379 | }; | ||
380 | |||
381 | // HTTP Headers | ||
382 | |||
383 | public const string HttpHeaderAccept = "Accept"; | ||
384 | public const string HttpHeaderAcceptCharset = "Accept-Charset"; | ||
385 | public const string HttpHeaderAcceptEncoding = "Accept-Encoding"; | ||
386 | public const string HttpHeaderAcceptLanguage = "Accept-Language"; | ||
387 | public const string HttpHeaderAcceptRanges = "Accept-Ranges"; | ||
388 | public const string HttpHeaderAge = "Age"; | ||
389 | public const string HttpHeaderAllow = "Allow"; | ||
390 | public const string HttpHeaderAuthorization = "Authorization"; | ||
391 | public const string HttpHeaderCacheControl = "Cache-Control"; | ||
392 | public const string HttpHeaderConnection = "Connection"; | ||
393 | public const string HttpHeaderContentEncoding = "Content-Encoding"; | ||
394 | public const string HttpHeaderContentLanguage = "Content-Language"; | ||
395 | public const string HttpHeaderContentLength = "Content-Length"; | ||
396 | public const string HttpHeaderContentLocation = "Content-Location"; | ||
397 | public const string HttpHeaderContentMD5 = "Content-MD5"; | ||
398 | public const string HttpHeaderContentRange = "Content-Range"; | ||
399 | public const string HttpHeaderContentType = "Content-Type"; | ||
400 | public const string HttpHeaderDate = "Date"; | ||
401 | public const string HttpHeaderETag = "ETag"; | ||
402 | public const string HttpHeaderExpect = "Expect"; | ||
403 | public const string HttpHeaderExpires = "Expires"; | ||
404 | public const string HttpHeaderFrom = "From"; | ||
405 | public const string HttpHeaderHost = "Host"; | ||
406 | public const string HttpHeaderIfMatch = "If-Match"; | ||
407 | public const string HttpHeaderIfModifiedSince = "If-Modified-Since"; | ||
408 | public const string HttpHeaderIfNoneMatch = "If-None-Match"; | ||
409 | public const string HttpHeaderIfRange = "If-Range"; | ||
410 | public const string HttpHeaderIfUnmodifiedSince = "If-Unmodified-Since"; | ||
411 | public const string HttpHeaderLastModified = "Last-Modified"; | ||
412 | public const string HttpHeaderLocation = "Location"; | ||
413 | public const string HttpHeaderMaxForwards = "Max-Forwards"; | ||
414 | public const string HttpHeaderPragma = "Pragma"; | ||
415 | public const string HttpHeaderProxyAuthenticate = "Proxy-Authenticate"; | ||
416 | public const string HttpHeaderProxyAuthorization = "Proxy-Authorization"; | ||
417 | public const string HttpHeaderRange = "Range"; | ||
418 | public const string HttpHeaderReferer = "Referer"; | ||
419 | public const string HttpHeaderRetryAfter = "Retry-After"; | ||
420 | public const string HttpHeaderServer = "Server"; | ||
421 | public const string HttpHeaderTE = "TE"; | ||
422 | public const string HttpHeaderTrailer = "Trailer"; | ||
423 | public const string HttpHeaderTransferEncoding = "Transfer-Encoding"; | ||
424 | public const string HttpHeaderUpgrade = "Upgrade"; | ||
425 | public const string HttpHeaderUserAgent = "User-Agent"; | ||
426 | public const string HttpHeaderVary = "Vary"; | ||
427 | public const string HttpHeaderVia = "Via"; | ||
428 | public const string HttpHeaderWarning = "Warning"; | ||
429 | public const string HttpHeaderWWWAuthenticate = "WWW-Authenticate"; | ||
430 | |||
431 | /// Utility routines | ||
432 | |||
433 | public static string StringToBase64(string str) | ||
434 | { | ||
435 | try | ||
436 | { | ||
437 | byte[] encData_byte = new byte[str.Length]; | ||
438 | encData_byte = Util.UTF8.GetBytes(str); | ||
439 | return Convert.ToBase64String(encData_byte); | ||
440 | } | ||
441 | catch | ||
442 | { | ||
443 | return String.Empty; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | public static string Base64ToString(string str) | ||
448 | { | ||
449 | try | ||
450 | { | ||
451 | return Util.Base64ToString(str); | ||
452 | } | ||
453 | catch | ||
454 | { | ||
455 | return String.Empty; | ||
456 | } | ||
457 | } | ||
458 | |||
459 | private const string hvals = "0123456789abcdef"; | ||
460 | |||
461 | public static int Hex2Int(string hex) | ||
462 | { | ||
463 | int val = 0; | ||
464 | int sum = 0; | ||
465 | string tmp = null; | ||
466 | |||
467 | if (hex != null) | ||
468 | { | ||
469 | tmp = hex.ToLower(); | ||
470 | for (int i = 0; i < tmp.Length; i++) | ||
471 | { | ||
472 | val = hvals.IndexOf(tmp[i]); | ||
473 | if (val == -1) | ||
474 | break; | ||
475 | sum *= 16; | ||
476 | sum += val; | ||
477 | } | ||
478 | } | ||
479 | |||
480 | return sum; | ||
481 | } | ||
482 | |||
483 | // Nonce management | ||
484 | |||
485 | public static string NonceGenerator() | ||
486 | { | ||
487 | return StringToBase64(CreationDate + Guid.NewGuid().ToString()); | ||
488 | } | ||
489 | |||
490 | // Dump the specified data stream | ||
491 | |||
492 | public static void Dump(byte[] data) | ||
493 | { | ||
494 | char[] buffer = new char[DumpLineSize]; | ||
495 | int cc = 0; | ||
496 | |||
497 | for (int i = 0; i < data.Length; i++) | ||
498 | { | ||
499 | if (i % DumpLineSize == 0) Console.Write("\n{0}: ",i.ToString("d8")); | ||
500 | |||
501 | if (i % 4 == 0) Console.Write(" "); | ||
502 | |||
503 | Console.Write("{0}",data[i].ToString("x2")); | ||
504 | |||
505 | if (data[i] < 127 && data[i] > 31) | ||
506 | buffer[i % DumpLineSize] = (char) data[i]; | ||
507 | else | ||
508 | buffer[i % DumpLineSize] = '.'; | ||
509 | |||
510 | cc++; | ||
511 | |||
512 | if (i != 0 && (i + 1) % DumpLineSize == 0) | ||
513 | { | ||
514 | Console.Write(" |"+(new String(buffer))+"|"); | ||
515 | cc = 0; | ||
516 | } | ||
517 | } | ||
518 | |||
519 | // Finish off any incomplete line | ||
520 | |||
521 | if (cc != 0) | ||
522 | { | ||
523 | for (int i = cc ; i < DumpLineSize; i++) | ||
524 | { | ||
525 | if (i % 4 == 0) Console.Write(" "); | ||
526 | Console.Write(" "); | ||
527 | buffer[i % DumpLineSize] = ' '; | ||
528 | } | ||
529 | Console.WriteLine(" |"+(new String(buffer))+"|"); | ||
530 | } | ||
531 | else | ||
532 | { | ||
533 | Console.Write("\n"); | ||
534 | } | ||
535 | } | ||
536 | } | ||
537 | |||
538 | // Local exception type | ||
539 | |||
540 | public class RestException : Exception | ||
541 | { | ||
542 | internal int statusCode; | ||
543 | internal string statusDesc; | ||
544 | internal string httpmethod; | ||
545 | internal string httppath; | ||
546 | |||
547 | public RestException(string msg) : base(msg) | ||
548 | { | ||
549 | } | ||
550 | } | ||
551 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs deleted file mode 100644 index 3cda984..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs +++ /dev/null | |||
@@ -1,860 +0,0 @@ | |||
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.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Xml; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Framework.Servers; | ||
35 | using OpenSim.Framework.Servers.HttpServer; | ||
36 | using OpenSim.Services.Interfaces; | ||
37 | |||
38 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
39 | { | ||
40 | |||
41 | public class RestAppearanceServices : IRest | ||
42 | { | ||
43 | // private static readonly int PARM_USERID = 0; | ||
44 | |||
45 | // private static readonly int PARM_PATH = 1; | ||
46 | |||
47 | // private bool enabled = false; | ||
48 | private string qPrefix = "appearance"; | ||
49 | |||
50 | /// <summary> | ||
51 | /// The constructor makes sure that the service prefix is absolute | ||
52 | /// and the registers the service handler and the allocator. | ||
53 | /// </summary> | ||
54 | |||
55 | public RestAppearanceServices() | ||
56 | { | ||
57 | Rest.Log.InfoFormat("{0} User appearance services initializing", MsgId); | ||
58 | Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); | ||
59 | |||
60 | // If a relative path was specified for the handler's domain, | ||
61 | // add the standard prefix to make it absolute, e.g. /admin | ||
62 | |||
63 | if (!qPrefix.StartsWith(Rest.UrlPathSeparator)) | ||
64 | { | ||
65 | Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId); | ||
66 | qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix); | ||
67 | qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix); | ||
68 | Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix); | ||
69 | } | ||
70 | |||
71 | // Register interface using the absolute URI. | ||
72 | |||
73 | Rest.Plugin.AddPathHandler(DoAppearance,qPrefix,Allocate); | ||
74 | |||
75 | // Activate if everything went OK | ||
76 | |||
77 | // enabled = true; | ||
78 | |||
79 | Rest.Log.InfoFormat("{0} User appearance services initialization complete", MsgId); | ||
80 | } | ||
81 | |||
82 | /// <summary> | ||
83 | /// Post-construction, pre-enabled initialization opportunity | ||
84 | /// Not currently exploited. | ||
85 | /// </summary> | ||
86 | |||
87 | public void Initialize() | ||
88 | { | ||
89 | } | ||
90 | |||
91 | /// <summary> | ||
92 | /// Called by the plug-in to halt service processing. Local processing is | ||
93 | /// disabled. | ||
94 | /// </summary> | ||
95 | |||
96 | public void Close() | ||
97 | { | ||
98 | // enabled = false; | ||
99 | Rest.Log.InfoFormat("{0} User appearance services closing down", MsgId); | ||
100 | } | ||
101 | |||
102 | /// <summary> | ||
103 | /// This property is declared locally because it is used a lot and | ||
104 | /// brevity is nice. | ||
105 | /// </summary> | ||
106 | |||
107 | internal string MsgId | ||
108 | { | ||
109 | get { return Rest.MsgId; } | ||
110 | } | ||
111 | |||
112 | #region Interface | ||
113 | |||
114 | /// <summary> | ||
115 | /// The plugin (RestHandler) calls this method to allocate the request | ||
116 | /// state carrier for a new request. It is destroyed when the request | ||
117 | /// completes. All request-instance specific state is kept here. This | ||
118 | /// is registered when this service provider is registered. | ||
119 | /// </summary> | ||
120 | /// <param name=request>Inbound HTTP request information</param> | ||
121 | /// <param name=response>Outbound HTTP request information</param> | ||
122 | /// <param name=qPrefix>REST service domain prefix</param> | ||
123 | /// <returns>A RequestData instance suitable for this service</returns> | ||
124 | |||
125 | private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
126 | { | ||
127 | return (RequestData) new AppearanceRequestData(request, response, prefix); | ||
128 | } | ||
129 | |||
130 | /// <summary> | ||
131 | /// This method is registered with the handler when this service provider | ||
132 | /// is initialized. It is called whenever the plug-in identifies this service | ||
133 | /// provider as the best match for a given request. | ||
134 | /// It handles all aspects of inventory REST processing, i.e. /admin/inventory | ||
135 | /// </summary> | ||
136 | /// <param name=hdata>A consolidated HTTP request work area</param> | ||
137 | |||
138 | private void DoAppearance(RequestData hdata) | ||
139 | { | ||
140 | // !!! REFACTORIMG PROBLEM. This needs rewriting for 0.7 | ||
141 | |||
142 | //AppearanceRequestData rdata = (AppearanceRequestData) hdata; | ||
143 | |||
144 | //Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId); | ||
145 | |||
146 | //// If we're disabled, do nothing. | ||
147 | |||
148 | //if (!enabled) | ||
149 | //{ | ||
150 | // return; | ||
151 | //} | ||
152 | |||
153 | //// Now that we know this is a serious attempt to | ||
154 | //// access inventory data, we should find out who | ||
155 | //// is asking, and make sure they are authorized | ||
156 | //// to do so. We need to validate the caller's | ||
157 | //// identity before revealing anything about the | ||
158 | //// status quo. Authenticate throws an exception | ||
159 | //// via Fail if no identity information is present. | ||
160 | //// | ||
161 | //// With the present HTTP server we can't use the | ||
162 | //// builtin authentication mechanisms because they | ||
163 | //// would be enforced for all in-bound requests. | ||
164 | //// Instead we look at the headers ourselves and | ||
165 | //// handle authentication directly. | ||
166 | |||
167 | //try | ||
168 | //{ | ||
169 | // if (!rdata.IsAuthenticated) | ||
170 | // { | ||
171 | // rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName)); | ||
172 | // } | ||
173 | //} | ||
174 | //catch (RestException e) | ||
175 | //{ | ||
176 | // if (e.statusCode == Rest.HttpStatusCodeNotAuthorized) | ||
177 | // { | ||
178 | // Rest.Log.WarnFormat("{0} User not authenticated", MsgId); | ||
179 | // Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization")); | ||
180 | // } | ||
181 | // else | ||
182 | // { | ||
183 | // Rest.Log.ErrorFormat("{0} User authentication failed", MsgId); | ||
184 | // Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization")); | ||
185 | // } | ||
186 | // throw (e); | ||
187 | //} | ||
188 | |||
189 | //Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName); | ||
190 | |||
191 | //// We can only get here if we are authorized | ||
192 | //// | ||
193 | //// The requestor may have specified an UUID or | ||
194 | //// a conjoined FirstName LastName string. We'll | ||
195 | //// try both. If we fail with the first, UUID, | ||
196 | //// attempt, we try the other. As an example, the | ||
197 | //// URI for a valid inventory request might be: | ||
198 | //// | ||
199 | //// http://<host>:<port>/admin/inventory/Arthur Dent | ||
200 | //// | ||
201 | //// Indicating that this is an inventory request for | ||
202 | //// an avatar named Arthur Dent. This is ALL that is | ||
203 | //// required to designate a GET for an entire | ||
204 | //// inventory. | ||
205 | //// | ||
206 | |||
207 | //// Do we have at least a user agent name? | ||
208 | |||
209 | //if (rdata.Parameters.Length < 1) | ||
210 | //{ | ||
211 | // Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId); | ||
212 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified"); | ||
213 | //} | ||
214 | |||
215 | //// The first parameter MUST be the agent identification, either an UUID | ||
216 | //// or a space-separated First-name Last-Name specification. We check for | ||
217 | //// an UUID first, if anyone names their character using a valid UUID | ||
218 | //// that identifies another existing avatar will cause this a problem... | ||
219 | |||
220 | //try | ||
221 | //{ | ||
222 | // rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]); | ||
223 | // Rest.Log.DebugFormat("{0} UUID supplied", MsgId); | ||
224 | // rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid); | ||
225 | //} | ||
226 | //catch | ||
227 | //{ | ||
228 | // string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE); | ||
229 | // if (names.Length == 2) | ||
230 | // { | ||
231 | // Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId); | ||
232 | // rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]); | ||
233 | // } | ||
234 | // else | ||
235 | // { | ||
236 | // Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId); | ||
237 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity"); | ||
238 | // } | ||
239 | //} | ||
240 | |||
241 | //// If the user profile is null then either the server is broken, or the | ||
242 | //// user is not known. We always assume the latter case. | ||
243 | |||
244 | //if (rdata.userProfile != null) | ||
245 | //{ | ||
246 | // Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}", | ||
247 | // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | ||
248 | //} | ||
249 | //else | ||
250 | //{ | ||
251 | // Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path); | ||
252 | // rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity"); | ||
253 | //} | ||
254 | |||
255 | //// If we get to here, then we have effectively validated the user's | ||
256 | |||
257 | //switch (rdata.method) | ||
258 | //{ | ||
259 | // case Rest.HEAD : // Do the processing, set the status code, suppress entity | ||
260 | // DoGet(rdata); | ||
261 | // rdata.buffer = null; | ||
262 | // break; | ||
263 | |||
264 | // case Rest.GET : // Do the processing, set the status code, return entity | ||
265 | // DoGet(rdata); | ||
266 | // break; | ||
267 | |||
268 | // case Rest.PUT : // Update named element | ||
269 | // DoUpdate(rdata); | ||
270 | // break; | ||
271 | |||
272 | // case Rest.POST : // Add new information to identified context. | ||
273 | // DoExtend(rdata); | ||
274 | // break; | ||
275 | |||
276 | // case Rest.DELETE : // Delete information | ||
277 | // DoDelete(rdata); | ||
278 | // break; | ||
279 | |||
280 | // default : | ||
281 | // Rest.Log.WarnFormat("{0} Method {1} not supported for {2}", | ||
282 | // MsgId, rdata.method, rdata.path); | ||
283 | // rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed, | ||
284 | // String.Format("{0} not supported", rdata.method)); | ||
285 | // break; | ||
286 | //} | ||
287 | } | ||
288 | |||
289 | #endregion Interface | ||
290 | |||
291 | #region method-specific processing | ||
292 | |||
293 | /// <summary> | ||
294 | /// This method implements GET processing for user's appearance. | ||
295 | /// </summary> | ||
296 | /// <param name=rdata>HTTP service request work area</param> | ||
297 | |||
298 | // private void DoGet(AppearanceRequestData rdata) | ||
299 | // { | ||
300 | // AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID); | ||
301 | // | ||
302 | // if (adata == null) | ||
303 | // { | ||
304 | // rdata.Fail(Rest.HttpStatusCodeNoContent, | ||
305 | // String.Format("appearance data not found for user {0} {1}", | ||
306 | // rdata.userProfile.FirstName, rdata.userProfile.SurName)); | ||
307 | // } | ||
308 | // rdata.userAppearance = adata.ToAvatarAppearance(rdata.userProfile.ID); | ||
309 | // | ||
310 | // rdata.initXmlWriter(); | ||
311 | // | ||
312 | // FormatUserAppearance(rdata); | ||
313 | // | ||
314 | // // Indicate a successful request | ||
315 | // | ||
316 | // rdata.Complete(); | ||
317 | // | ||
318 | // // Send the response to the user. The body will be implicitly | ||
319 | // // constructed from the result of the XML writer. | ||
320 | // | ||
321 | // rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method)); | ||
322 | // } | ||
323 | |||
324 | /// <summary> | ||
325 | /// POST adds NEW information to the user profile database. | ||
326 | /// This effectively resets the appearance before applying those | ||
327 | /// characteristics supplied in the request. | ||
328 | /// </summary> | ||
329 | |||
330 | // private void DoExtend(AppearanceRequestData rdata) | ||
331 | // { | ||
332 | // | ||
333 | // bool created = false; | ||
334 | // bool modified = false; | ||
335 | // string newnode = String.Empty; | ||
336 | // | ||
337 | // Rest.Log.DebugFormat("{0} POST ENTRY", MsgId); | ||
338 | // | ||
339 | // //AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID); | ||
340 | // | ||
341 | // rdata.userAppearance = new AvatarAppearance(); | ||
342 | // | ||
343 | // // Although the following behavior is admitted by HTTP I am becoming | ||
344 | // // increasingly doubtful that it is appropriate for REST. If I attempt to | ||
345 | // // add a new record, and it already exists, then it seems to me that the | ||
346 | // // attempt should fail, rather than update the existing record. | ||
347 | // AvatarData adata = null; | ||
348 | // if (GetUserAppearance(rdata)) | ||
349 | // { | ||
350 | // modified = rdata.userAppearance != null; | ||
351 | // created = !modified; | ||
352 | // adata = new AvatarData(rdata.userAppearance); | ||
353 | // Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); | ||
354 | // // Rest.UserServices.UpdateUserProfile(rdata.userProfile); | ||
355 | // } | ||
356 | // else | ||
357 | // { | ||
358 | // created = true; | ||
359 | // adata = new AvatarData(rdata.userAppearance); | ||
360 | // Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); | ||
361 | // // Rest.UserServices.UpdateUserProfile(rdata.userProfile); | ||
362 | // } | ||
363 | // | ||
364 | // if (created) | ||
365 | // { | ||
366 | // newnode = String.Format("{0} {1}", rdata.userProfile.FirstName, | ||
367 | // rdata.userProfile.SurName); | ||
368 | // // Must include a location header with a URI that identifies the new resource. | ||
369 | // | ||
370 | // rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}", | ||
371 | // rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode)); | ||
372 | // rdata.Complete(Rest.HttpStatusCodeCreated); | ||
373 | // | ||
374 | // } | ||
375 | // else | ||
376 | // { | ||
377 | // if (modified) | ||
378 | // { | ||
379 | // rdata.Complete(Rest.HttpStatusCodeOK); | ||
380 | // } | ||
381 | // else | ||
382 | // { | ||
383 | // rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
384 | // } | ||
385 | // } | ||
386 | // | ||
387 | // rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); | ||
388 | // | ||
389 | // } | ||
390 | |||
391 | /// <summary> | ||
392 | /// This updates the user's appearance. not all aspects need to be provided, | ||
393 | /// only those supplied will be changed. | ||
394 | /// </summary> | ||
395 | |||
396 | // private void DoUpdate(AppearanceRequestData rdata) | ||
397 | // { | ||
398 | // | ||
399 | // // REFACTORING PROBLEM This was commented out. It doesn't work for 0.7 | ||
400 | // | ||
401 | // //bool created = false; | ||
402 | // //bool modified = false; | ||
403 | // | ||
404 | // | ||
405 | // //rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID); | ||
406 | // | ||
407 | // //// If the user exists then this is considered a modification regardless | ||
408 | // //// of what may, or may not be, specified in the payload. | ||
409 | // | ||
410 | // //if (rdata.userAppearance != null) | ||
411 | // //{ | ||
412 | // // modified = true; | ||
413 | // // Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance); | ||
414 | // // Rest.UserServices.UpdateUserProfile(rdata.userProfile); | ||
415 | // //} | ||
416 | // | ||
417 | // //if (created) | ||
418 | // //{ | ||
419 | // // rdata.Complete(Rest.HttpStatusCodeCreated); | ||
420 | // //} | ||
421 | // //else | ||
422 | // //{ | ||
423 | // // if (modified) | ||
424 | // // { | ||
425 | // // rdata.Complete(Rest.HttpStatusCodeOK); | ||
426 | // // } | ||
427 | // // else | ||
428 | // // { | ||
429 | // // rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
430 | // // } | ||
431 | // //} | ||
432 | // | ||
433 | // rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); | ||
434 | // | ||
435 | // } | ||
436 | |||
437 | /// <summary> | ||
438 | /// Delete the specified user's appearance. This actually performs a reset | ||
439 | /// to the default avatar appearance, if the info is already there. | ||
440 | /// Existing ownership is preserved. All prior updates are lost and can not | ||
441 | /// be recovered. | ||
442 | /// </summary> | ||
443 | // private void DoDelete(AppearanceRequestData rdata) | ||
444 | // { | ||
445 | // AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID); | ||
446 | // | ||
447 | // if (adata != null) | ||
448 | // { | ||
449 | // AvatarAppearance old = adata.ToAvatarAppearance(rdata.userProfile.ID); | ||
450 | // rdata.userAppearance = new AvatarAppearance(); | ||
451 | // rdata.userAppearance.Owner = old.Owner; | ||
452 | // adata = new AvatarData(rdata.userAppearance); | ||
453 | // | ||
454 | // Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); | ||
455 | // | ||
456 | // rdata.Complete(); | ||
457 | // } | ||
458 | // else | ||
459 | // { | ||
460 | // | ||
461 | // rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
462 | // } | ||
463 | // | ||
464 | // rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); | ||
465 | // } | ||
466 | |||
467 | #endregion method-specific processing | ||
468 | |||
469 | private bool GetUserAppearance(AppearanceRequestData rdata) | ||
470 | { | ||
471 | |||
472 | XmlReader xml; | ||
473 | bool indata = false; | ||
474 | |||
475 | rdata.initXmlReader(); | ||
476 | xml = rdata.reader; | ||
477 | |||
478 | while (xml.Read()) | ||
479 | { | ||
480 | switch (xml.NodeType) | ||
481 | { | ||
482 | case XmlNodeType.Element : | ||
483 | switch (xml.Name) | ||
484 | { | ||
485 | case "Appearance" : | ||
486 | if (xml.MoveToAttribute("Height")) | ||
487 | { | ||
488 | rdata.userAppearance.AvatarHeight = (float) Convert.ToDouble(xml.Value); | ||
489 | indata = true; | ||
490 | } | ||
491 | // if (xml.MoveToAttribute("Owner")) | ||
492 | // { | ||
493 | // rdata.userAppearance.Owner = (UUID)xml.Value; | ||
494 | // indata = true; | ||
495 | // } | ||
496 | if (xml.MoveToAttribute("Serial")) | ||
497 | { | ||
498 | rdata.userAppearance.Serial = Convert.ToInt32(xml.Value); | ||
499 | indata = true; | ||
500 | } | ||
501 | break; | ||
502 | /* | ||
503 | case "Body" : | ||
504 | if (xml.MoveToAttribute("Item")) | ||
505 | { | ||
506 | rdata.userAppearance.BodyItem = (UUID)xml.Value; | ||
507 | indata = true; | ||
508 | } | ||
509 | if (xml.MoveToAttribute("Asset")) | ||
510 | { | ||
511 | rdata.userAppearance.BodyAsset = (UUID)xml.Value; | ||
512 | indata = true; | ||
513 | } | ||
514 | break; | ||
515 | case "Skin" : | ||
516 | if (xml.MoveToAttribute("Item")) | ||
517 | { | ||
518 | rdata.userAppearance.SkinItem = (UUID)xml.Value; | ||
519 | indata = true; | ||
520 | } | ||
521 | if (xml.MoveToAttribute("Asset")) | ||
522 | { | ||
523 | rdata.userAppearance.SkinAsset = (UUID)xml.Value; | ||
524 | indata = true; | ||
525 | } | ||
526 | break; | ||
527 | case "Hair" : | ||
528 | if (xml.MoveToAttribute("Item")) | ||
529 | { | ||
530 | rdata.userAppearance.HairItem = (UUID)xml.Value; | ||
531 | indata = true; | ||
532 | } | ||
533 | if (xml.MoveToAttribute("Asset")) | ||
534 | { | ||
535 | rdata.userAppearance.HairAsset = (UUID)xml.Value; | ||
536 | indata = true; | ||
537 | } | ||
538 | break; | ||
539 | case "Eyes" : | ||
540 | if (xml.MoveToAttribute("Item")) | ||
541 | { | ||
542 | rdata.userAppearance.EyesItem = (UUID)xml.Value; | ||
543 | indata = true; | ||
544 | } | ||
545 | if (xml.MoveToAttribute("Asset")) | ||
546 | { | ||
547 | rdata.userAppearance.EyesAsset = (UUID)xml.Value; | ||
548 | indata = true; | ||
549 | } | ||
550 | break; | ||
551 | case "Shirt" : | ||
552 | if (xml.MoveToAttribute("Item")) | ||
553 | { | ||
554 | rdata.userAppearance.ShirtItem = (UUID)xml.Value; | ||
555 | indata = true; | ||
556 | } | ||
557 | if (xml.MoveToAttribute("Asset")) | ||
558 | { | ||
559 | rdata.userAppearance.ShirtAsset = (UUID)xml.Value; | ||
560 | indata = true; | ||
561 | } | ||
562 | break; | ||
563 | case "Pants" : | ||
564 | if (xml.MoveToAttribute("Item")) | ||
565 | { | ||
566 | rdata.userAppearance.PantsItem = (UUID)xml.Value; | ||
567 | indata = true; | ||
568 | } | ||
569 | if (xml.MoveToAttribute("Asset")) | ||
570 | { | ||
571 | rdata.userAppearance.PantsAsset = (UUID)xml.Value; | ||
572 | indata = true; | ||
573 | } | ||
574 | break; | ||
575 | case "Shoes" : | ||
576 | if (xml.MoveToAttribute("Item")) | ||
577 | { | ||
578 | rdata.userAppearance.ShoesItem = (UUID)xml.Value; | ||
579 | indata = true; | ||
580 | } | ||
581 | if (xml.MoveToAttribute("Asset")) | ||
582 | { | ||
583 | rdata.userAppearance.ShoesAsset = (UUID)xml.Value; | ||
584 | indata = true; | ||
585 | } | ||
586 | break; | ||
587 | case "Socks" : | ||
588 | if (xml.MoveToAttribute("Item")) | ||
589 | { | ||
590 | rdata.userAppearance.SocksItem = (UUID)xml.Value; | ||
591 | indata = true; | ||
592 | } | ||
593 | if (xml.MoveToAttribute("Asset")) | ||
594 | { | ||
595 | rdata.userAppearance.SocksAsset = (UUID)xml.Value; | ||
596 | indata = true; | ||
597 | } | ||
598 | break; | ||
599 | case "Jacket" : | ||
600 | if (xml.MoveToAttribute("Item")) | ||
601 | { | ||
602 | rdata.userAppearance.JacketItem = (UUID)xml.Value; | ||
603 | indata = true; | ||
604 | } | ||
605 | if (xml.MoveToAttribute("Asset")) | ||
606 | { | ||
607 | rdata.userAppearance.JacketAsset = (UUID)xml.Value; | ||
608 | indata = true; | ||
609 | } | ||
610 | break; | ||
611 | case "Gloves" : | ||
612 | if (xml.MoveToAttribute("Item")) | ||
613 | { | ||
614 | rdata.userAppearance.GlovesItem = (UUID)xml.Value; | ||
615 | indata = true; | ||
616 | } | ||
617 | if (xml.MoveToAttribute("Asset")) | ||
618 | { | ||
619 | rdata.userAppearance.GlovesAsset = (UUID)xml.Value; | ||
620 | indata = true; | ||
621 | } | ||
622 | break; | ||
623 | case "UnderShirt" : | ||
624 | if (xml.MoveToAttribute("Item")) | ||
625 | { | ||
626 | rdata.userAppearance.UnderShirtItem = (UUID)xml.Value; | ||
627 | indata = true; | ||
628 | } | ||
629 | if (xml.MoveToAttribute("Asset")) | ||
630 | { | ||
631 | rdata.userAppearance.UnderShirtAsset = (UUID)xml.Value; | ||
632 | indata = true; | ||
633 | } | ||
634 | break; | ||
635 | case "UnderPants" : | ||
636 | if (xml.MoveToAttribute("Item")) | ||
637 | { | ||
638 | rdata.userAppearance.UnderPantsItem = (UUID)xml.Value; | ||
639 | indata = true; | ||
640 | } | ||
641 | if (xml.MoveToAttribute("Asset")) | ||
642 | { | ||
643 | rdata.userAppearance.UnderPantsAsset = (UUID)xml.Value; | ||
644 | indata = true; | ||
645 | } | ||
646 | break; | ||
647 | case "Skirt" : | ||
648 | if (xml.MoveToAttribute("Item")) | ||
649 | { | ||
650 | rdata.userAppearance.SkirtItem = (UUID)xml.Value; | ||
651 | indata = true; | ||
652 | } | ||
653 | if (xml.MoveToAttribute("Asset")) | ||
654 | { | ||
655 | rdata.userAppearance.SkirtAsset = (UUID)xml.Value; | ||
656 | indata = true; | ||
657 | } | ||
658 | break; | ||
659 | */ | ||
660 | case "Attachment" : | ||
661 | { | ||
662 | |||
663 | int ap; | ||
664 | UUID asset; | ||
665 | UUID item; | ||
666 | |||
667 | if (xml.MoveToAttribute("AtPoint")) | ||
668 | { | ||
669 | ap = Convert.ToInt32(xml.Value); | ||
670 | if (xml.MoveToAttribute("Asset")) | ||
671 | { | ||
672 | asset = new UUID(xml.Value); | ||
673 | if (xml.MoveToAttribute("Asset")) | ||
674 | { | ||
675 | item = new UUID(xml.Value); | ||
676 | rdata.userAppearance.SetAttachment(ap, item, asset); | ||
677 | indata = true; | ||
678 | } | ||
679 | } | ||
680 | } | ||
681 | } | ||
682 | break; | ||
683 | case "Texture" : | ||
684 | if (xml.MoveToAttribute("Default")) | ||
685 | { | ||
686 | rdata.userAppearance.Texture = new Primitive.TextureEntry(new UUID(xml.Value)); | ||
687 | indata = true; | ||
688 | } | ||
689 | break; | ||
690 | case "Face" : | ||
691 | { | ||
692 | uint index; | ||
693 | if (xml.MoveToAttribute("Index")) | ||
694 | { | ||
695 | index = Convert.ToUInt32(xml.Value); | ||
696 | if (xml.MoveToAttribute("Id")) | ||
697 | { | ||
698 | rdata.userAppearance.Texture.CreateFace(index).TextureID = new UUID(xml.Value); | ||
699 | indata = true; | ||
700 | } | ||
701 | } | ||
702 | } | ||
703 | break; | ||
704 | case "VisualParameters" : | ||
705 | { | ||
706 | xml.ReadContentAsBase64(rdata.userAppearance.VisualParams, | ||
707 | 0, rdata.userAppearance.VisualParams.Length); | ||
708 | indata = true; | ||
709 | } | ||
710 | break; | ||
711 | } | ||
712 | break; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | return indata; | ||
717 | |||
718 | } | ||
719 | |||
720 | private void FormatPart(AppearanceRequestData rdata, string part, UUID item, UUID asset) | ||
721 | { | ||
722 | if (item != UUID.Zero || asset != UUID.Zero) | ||
723 | { | ||
724 | rdata.writer.WriteStartElement(part); | ||
725 | if (item != UUID.Zero) | ||
726 | { | ||
727 | rdata.writer.WriteAttributeString("Item",item.ToString()); | ||
728 | } | ||
729 | |||
730 | if (asset != UUID.Zero) | ||
731 | { | ||
732 | rdata.writer.WriteAttributeString("Asset",asset.ToString()); | ||
733 | } | ||
734 | rdata.writer.WriteEndElement(); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | private void FormatUserAppearance(AppearanceRequestData rdata) | ||
739 | { | ||
740 | |||
741 | Rest.Log.DebugFormat("{0} FormatUserAppearance", MsgId); | ||
742 | |||
743 | if (rdata.userAppearance != null) | ||
744 | { | ||
745 | |||
746 | Rest.Log.DebugFormat("{0} FormatUserAppearance: appearance object exists", MsgId); | ||
747 | rdata.writer.WriteStartElement("Appearance"); | ||
748 | |||
749 | rdata.writer.WriteAttributeString("Height", rdata.userAppearance.AvatarHeight.ToString()); | ||
750 | // if (rdata.userAppearance.Owner != UUID.Zero) | ||
751 | // rdata.writer.WriteAttributeString("Owner", rdata.userAppearance.Owner.ToString()); | ||
752 | rdata.writer.WriteAttributeString("Serial", rdata.userAppearance.Serial.ToString()); | ||
753 | |||
754 | /* | ||
755 | FormatPart(rdata, "Body", rdata.userAppearance.BodyItem, rdata.userAppearance.BodyAsset); | ||
756 | FormatPart(rdata, "Skin", rdata.userAppearance.SkinItem, rdata.userAppearance.SkinAsset); | ||
757 | FormatPart(rdata, "Hair", rdata.userAppearance.HairItem, rdata.userAppearance.HairAsset); | ||
758 | FormatPart(rdata, "Eyes", rdata.userAppearance.EyesItem, rdata.userAppearance.EyesAsset); | ||
759 | |||
760 | FormatPart(rdata, "Shirt", rdata.userAppearance.ShirtItem, rdata.userAppearance.ShirtAsset); | ||
761 | FormatPart(rdata, "Pants", rdata.userAppearance.PantsItem, rdata.userAppearance.PantsAsset); | ||
762 | FormatPart(rdata, "Skirt", rdata.userAppearance.SkirtItem, rdata.userAppearance.SkirtAsset); | ||
763 | FormatPart(rdata, "Shoes", rdata.userAppearance.ShoesItem, rdata.userAppearance.ShoesAsset); | ||
764 | FormatPart(rdata, "Socks", rdata.userAppearance.SocksItem, rdata.userAppearance.SocksAsset); | ||
765 | |||
766 | FormatPart(rdata, "Jacket", rdata.userAppearance.JacketItem, rdata.userAppearance.JacketAsset); | ||
767 | FormatPart(rdata, "Gloves", rdata.userAppearance.GlovesItem, rdata.userAppearance.GlovesAsset); | ||
768 | |||
769 | FormatPart(rdata, "UnderShirt", rdata.userAppearance.UnderShirtItem, rdata.userAppearance.UnderShirtAsset); | ||
770 | FormatPart(rdata, "UnderPants", rdata.userAppearance.UnderPantsItem, rdata.userAppearance.UnderPantsAsset); | ||
771 | */ | ||
772 | Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting attachments", MsgId); | ||
773 | |||
774 | rdata.writer.WriteStartElement("Attachments"); | ||
775 | List<AvatarAttachment> attachments = rdata.userAppearance.GetAttachments(); | ||
776 | foreach (AvatarAttachment attach in attachments) | ||
777 | { | ||
778 | rdata.writer.WriteStartElement("Attachment"); | ||
779 | rdata.writer.WriteAttributeString("AtPoint", attach.AttachPoint.ToString()); | ||
780 | rdata.writer.WriteAttributeString("Item", attach.ItemID.ToString()); | ||
781 | rdata.writer.WriteAttributeString("Asset", attach.AssetID.ToString()); | ||
782 | rdata.writer.WriteEndElement(); | ||
783 | } | ||
784 | rdata.writer.WriteEndElement(); | ||
785 | |||
786 | Primitive.TextureEntry texture = rdata.userAppearance.Texture; | ||
787 | |||
788 | if (texture != null && (texture.DefaultTexture != null || texture.FaceTextures != null)) | ||
789 | { | ||
790 | Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting textures", MsgId); | ||
791 | |||
792 | rdata.writer.WriteStartElement("Texture"); | ||
793 | |||
794 | if (texture.DefaultTexture != null) | ||
795 | { | ||
796 | Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting default texture", MsgId); | ||
797 | rdata.writer.WriteAttributeString("Default", | ||
798 | texture.DefaultTexture.TextureID.ToString()); | ||
799 | } | ||
800 | |||
801 | if (texture.FaceTextures != null) | ||
802 | { | ||
803 | |||
804 | Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting face textures", MsgId); | ||
805 | |||
806 | for (int i=0; i<texture.FaceTextures.Length;i++) | ||
807 | { | ||
808 | if (texture.FaceTextures[i] != null) | ||
809 | { | ||
810 | rdata.writer.WriteStartElement("Face"); | ||
811 | rdata.writer.WriteAttributeString("Index", i.ToString()); | ||
812 | rdata.writer.WriteAttributeString("Id", | ||
813 | texture.FaceTextures[i].TextureID.ToString()); | ||
814 | rdata.writer.WriteEndElement(); | ||
815 | } | ||
816 | } | ||
817 | } | ||
818 | |||
819 | rdata.writer.WriteEndElement(); | ||
820 | } | ||
821 | |||
822 | Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting visual parameters", MsgId); | ||
823 | |||
824 | rdata.writer.WriteStartElement("VisualParameters"); | ||
825 | rdata.writer.WriteBase64(rdata.userAppearance.VisualParams,0, | ||
826 | rdata.userAppearance.VisualParams.Length); | ||
827 | rdata.writer.WriteEndElement(); | ||
828 | rdata.writer.WriteFullEndElement(); | ||
829 | } | ||
830 | |||
831 | Rest.Log.DebugFormat("{0} FormatUserAppearance: completed", MsgId); | ||
832 | |||
833 | return; | ||
834 | } | ||
835 | |||
836 | #region appearance RequestData extension | ||
837 | |||
838 | internal class AppearanceRequestData : RequestData | ||
839 | { | ||
840 | |||
841 | /// <summary> | ||
842 | /// These are the inventory specific request/response state | ||
843 | /// extensions. | ||
844 | /// </summary> | ||
845 | |||
846 | internal UUID uuid = UUID.Zero; | ||
847 | internal UserProfileData userProfile = null; | ||
848 | internal AvatarAppearance userAppearance = null; | ||
849 | |||
850 | internal AppearanceRequestData(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
851 | : base(request, response, prefix) | ||
852 | { | ||
853 | } | ||
854 | |||
855 | } | ||
856 | |||
857 | #endregion Appearance RequestData extension | ||
858 | |||
859 | } | ||
860 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAssetServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestAssetServices.cs deleted file mode 100644 index 4ba3d77..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAssetServices.cs +++ /dev/null | |||
@@ -1,383 +0,0 @@ | |||
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.Xml; | ||
30 | using OpenMetaverse; | ||
31 | using OpenSim.Framework; | ||
32 | using OpenSim.Framework.Servers; | ||
33 | using OpenSim.Framework.Servers.HttpServer; | ||
34 | |||
35 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
36 | { | ||
37 | public class RestAssetServices : IRest | ||
38 | { | ||
39 | private bool enabled = false; | ||
40 | private string qPrefix = "assets"; | ||
41 | |||
42 | // A simple constructor is used to handle any once-only | ||
43 | // initialization of working classes. | ||
44 | |||
45 | public RestAssetServices() | ||
46 | { | ||
47 | Rest.Log.InfoFormat("{0} Asset 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(DoAsset, qPrefix, Allocate); | ||
63 | |||
64 | // Activate if all went OK | ||
65 | |||
66 | enabled = true; | ||
67 | |||
68 | Rest.Log.InfoFormat("{0} Asset 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} Asset 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 AssetRequestData(request, response, prefix); | ||
100 | } | ||
101 | |||
102 | // Asset Handler | ||
103 | |||
104 | private void DoAsset(RequestData rparm) | ||
105 | { | ||
106 | if (!enabled) return; | ||
107 | |||
108 | AssetRequestData rdata = (AssetRequestData) rparm; | ||
109 | |||
110 | Rest.Log.DebugFormat("{0} REST Asset handler ({1}) ENTRY", MsgId, qPrefix); | ||
111 | |||
112 | // Now that we know this is a serious attempt to | ||
113 | // access inventory 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 | default : | ||
169 | Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}", | ||
170 | MsgId, rdata.method); | ||
171 | rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method)); | ||
172 | break; | ||
173 | } | ||
174 | } | ||
175 | else | ||
176 | { | ||
177 | Rest.Log.WarnFormat("{0} Asset: No agent information provided", MsgId); | ||
178 | rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided"); | ||
179 | } | ||
180 | |||
181 | Rest.Log.DebugFormat("{0} REST Asset handler EXIT", MsgId); | ||
182 | } | ||
183 | |||
184 | #endregion Interface | ||
185 | |||
186 | /// <summary> | ||
187 | /// The only parameter we recognize is a UUID.If an asset with this identification is | ||
188 | /// found, it's content, base-64 encoded, is returned to the client. | ||
189 | /// </summary> | ||
190 | |||
191 | private void DoGet(AssetRequestData rdata) | ||
192 | { | ||
193 | Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method); | ||
194 | |||
195 | if (rdata.Parameters.Length == 1) | ||
196 | { | ||
197 | UUID uuid = new UUID(rdata.Parameters[0]); | ||
198 | AssetBase asset = Rest.AssetServices.Get(uuid.ToString()); | ||
199 | |||
200 | if (asset != null) | ||
201 | { | ||
202 | Rest.Log.DebugFormat("{0} Asset located <{1}>", MsgId, rdata.Parameters[0]); | ||
203 | |||
204 | rdata.initXmlWriter(); | ||
205 | |||
206 | rdata.writer.WriteStartElement(String.Empty,"Asset",String.Empty); | ||
207 | |||
208 | rdata.writer.WriteAttributeString("id", asset.ID); | ||
209 | rdata.writer.WriteAttributeString("name", asset.Name); | ||
210 | rdata.writer.WriteAttributeString("desc", asset.Description); | ||
211 | rdata.writer.WriteAttributeString("type", asset.Type.ToString()); | ||
212 | rdata.writer.WriteAttributeString("local", asset.Local.ToString()); | ||
213 | rdata.writer.WriteAttributeString("temporary", asset.Temporary.ToString()); | ||
214 | |||
215 | rdata.writer.WriteBase64(asset.Data,0,asset.Data.Length); | ||
216 | |||
217 | rdata.writer.WriteFullEndElement(); | ||
218 | |||
219 | } | ||
220 | else | ||
221 | { | ||
222 | Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path); | ||
223 | rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters"); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | rdata.Complete(); | ||
228 | rdata.Respond(String.Format("Asset <{0}> : Normal completion", rdata.method)); | ||
229 | |||
230 | } | ||
231 | |||
232 | /// <summary> | ||
233 | /// UPDATE existing item, if it exists. URI identifies the item in question. | ||
234 | /// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded) | ||
235 | /// is decoded and stored in the database, identified by the supplied UUID. | ||
236 | /// </summary> | ||
237 | private void DoPut(AssetRequestData rdata) | ||
238 | { | ||
239 | bool modified = false; | ||
240 | bool created = false; | ||
241 | |||
242 | AssetBase asset = null; | ||
243 | |||
244 | Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method); | ||
245 | |||
246 | if (rdata.Parameters.Length == 1) | ||
247 | { | ||
248 | |||
249 | rdata.initXmlReader(); | ||
250 | XmlReader xml = rdata.reader; | ||
251 | |||
252 | if (!xml.ReadToFollowing("Asset")) | ||
253 | { | ||
254 | Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path); | ||
255 | rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data"); | ||
256 | } | ||
257 | |||
258 | UUID uuid = new UUID(rdata.Parameters[0]); | ||
259 | asset = Rest.AssetServices.Get(uuid.ToString()); | ||
260 | |||
261 | modified = (asset != null); | ||
262 | created = !modified; | ||
263 | |||
264 | asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString()); | ||
265 | asset.Description = xml.GetAttribute("desc"); | ||
266 | asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0; | ||
267 | asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0; | ||
268 | asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", "")); | ||
269 | |||
270 | if (asset.ID != rdata.Parameters[0]) | ||
271 | { | ||
272 | Rest.Log.WarnFormat("{0} URI and payload disagree on UUID U:{1} vs P:{2}", | ||
273 | MsgId, rdata.Parameters[0], asset.ID); | ||
274 | } | ||
275 | |||
276 | Rest.AssetServices.Store(asset); | ||
277 | |||
278 | } | ||
279 | else | ||
280 | { | ||
281 | Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path); | ||
282 | rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters"); | ||
283 | } | ||
284 | |||
285 | if (created) | ||
286 | { | ||
287 | rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID)); | ||
288 | rdata.Complete(Rest.HttpStatusCodeCreated); | ||
289 | } | ||
290 | else | ||
291 | { | ||
292 | if (modified) | ||
293 | { | ||
294 | rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID)); | ||
295 | rdata.Complete(Rest.HttpStatusCodeOK); | ||
296 | } | ||
297 | else | ||
298 | { | ||
299 | rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method)); | ||
304 | |||
305 | } | ||
306 | |||
307 | /// <summary> | ||
308 | /// CREATE new item, replace if it exists. URI identifies the context for the item in question. | ||
309 | /// No parameters are required for POST, just thepayload. | ||
310 | /// </summary> | ||
311 | |||
312 | private void DoPost(AssetRequestData rdata) | ||
313 | { | ||
314 | |||
315 | bool modified = false; | ||
316 | bool created = false; | ||
317 | |||
318 | Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method); | ||
319 | |||
320 | if (rdata.Parameters.Length != 0) | ||
321 | { | ||
322 | Rest.Log.WarnFormat("{0} Parameters ignored <{1}>", MsgId, rdata.path); | ||
323 | Rest.Log.InfoFormat("{0} POST of an asset has no parameters", MsgId, rdata.path); | ||
324 | } | ||
325 | |||
326 | rdata.initXmlReader(); | ||
327 | XmlReader xml = rdata.reader; | ||
328 | |||
329 | if (!xml.ReadToFollowing("Asset")) | ||
330 | { | ||
331 | Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path); | ||
332 | rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data"); | ||
333 | } | ||
334 | |||
335 | UUID uuid = new UUID(xml.GetAttribute("id")); | ||
336 | AssetBase asset = Rest.AssetServices.Get(uuid.ToString()); | ||
337 | |||
338 | modified = (asset != null); | ||
339 | created = !modified; | ||
340 | |||
341 | asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString()); | ||
342 | asset.Description = xml.GetAttribute("desc"); | ||
343 | asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0; | ||
344 | asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0; | ||
345 | asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", "")); | ||
346 | |||
347 | Rest.AssetServices.Store(asset); | ||
348 | |||
349 | if (created) | ||
350 | { | ||
351 | rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID)); | ||
352 | rdata.Complete(Rest.HttpStatusCodeCreated); | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | if (modified) | ||
357 | { | ||
358 | rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID)); | ||
359 | rdata.Complete(Rest.HttpStatusCodeOK); | ||
360 | } | ||
361 | else | ||
362 | { | ||
363 | rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method)); | ||
368 | |||
369 | } | ||
370 | |||
371 | /// <summary> | ||
372 | /// Asset processing has no special data area requirements. | ||
373 | /// </summary> | ||
374 | |||
375 | internal class AssetRequestData : RequestData | ||
376 | { | ||
377 | internal AssetRequestData(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
378 | : base(request, response, prefix) | ||
379 | { | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs deleted file mode 100644 index e79d2bd..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs +++ /dev/null | |||
@@ -1,448 +0,0 @@ | |||
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.Xml; | ||
30 | using System.IO; | ||
31 | using OpenMetaverse; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Framework.Servers; | ||
34 | using OpenSim.Framework.Servers.HttpServer; | ||
35 | |||
36 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
37 | { | ||
38 | public class RestFileServices : IRest | ||
39 | { | ||
40 | private bool enabled = false; | ||
41 | private string qPrefix = "files"; | ||
42 | |||
43 | // A simple constructor is used to handle any once-only | ||
44 | // initialization of working classes. | ||
45 | |||
46 | public RestFileServices() | ||
47 | { | ||
48 | Rest.Log.InfoFormat("{0} File services initializing", MsgId); | ||
49 | Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); | ||
50 | |||
51 | // If the handler specifies a relative path for its domain | ||
52 | // then we must add the standard absolute prefix, e.g. /admin | ||
53 | |||
54 | if (!qPrefix.StartsWith(Rest.UrlPathSeparator)) | ||
55 | { | ||
56 | Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix); | ||
57 | qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix); | ||
58 | Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix); | ||
59 | } | ||
60 | |||
61 | // Register interface using the fully-qualified prefix | ||
62 | |||
63 | Rest.Plugin.AddPathHandler(DoFile, qPrefix, Allocate); | ||
64 | |||
65 | // Activate if all went OK | ||
66 | |||
67 | enabled = true; | ||
68 | |||
69 | Rest.Log.InfoFormat("{0} File services initialization complete", MsgId); | ||
70 | } | ||
71 | |||
72 | // Post-construction, pre-enabled initialization opportunity | ||
73 | // Not currently exploited. | ||
74 | |||
75 | public void Initialize() | ||
76 | { | ||
77 | } | ||
78 | |||
79 | // Called by the plug-in to halt REST processing. Local processing is | ||
80 | // disabled, and control blocks until all current processing has | ||
81 | // completed. No new processing will be started | ||
82 | |||
83 | public void Close() | ||
84 | { | ||
85 | enabled = false; | ||
86 | Rest.Log.InfoFormat("{0} File services ({1}) closing down", MsgId, qPrefix); | ||
87 | } | ||
88 | |||
89 | // Properties | ||
90 | |||
91 | internal string MsgId | ||
92 | { | ||
93 | get { return Rest.MsgId; } | ||
94 | } | ||
95 | |||
96 | #region Interface | ||
97 | |||
98 | private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
99 | { | ||
100 | return (RequestData) new FileRequestData(request, response, prefix); | ||
101 | } | ||
102 | |||
103 | // Asset Handler | ||
104 | |||
105 | private void DoFile(RequestData rparm) | ||
106 | { | ||
107 | if (!enabled) return; | ||
108 | |||
109 | FileRequestData rdata = (FileRequestData) rparm; | ||
110 | |||
111 | Rest.Log.DebugFormat("{0} REST File handler ({1}) ENTRY", MsgId, qPrefix); | ||
112 | |||
113 | // Now that we know this is a serious attempt to | ||
114 | // access file data, we should find out who | ||
115 | // is asking, and make sure they are authorized | ||
116 | // to do so. We need to validate the caller's | ||
117 | // identity before revealing anything about the | ||
118 | // status quo. Authenticate throws an exception | ||
119 | // via Fail if no identity information is present. | ||
120 | // | ||
121 | // With the present HTTP server we can't use the | ||
122 | // builtin authentication mechanisms because they | ||
123 | // would be enforced for all in-bound requests. | ||
124 | // Instead we look at the headers ourselves and | ||
125 | // handle authentication directly. | ||
126 | |||
127 | try | ||
128 | { | ||
129 | if (!rdata.IsAuthenticated) | ||
130 | { | ||
131 | rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated")); | ||
132 | } | ||
133 | } | ||
134 | catch (RestException e) | ||
135 | { | ||
136 | if (e.statusCode == Rest.HttpStatusCodeNotAuthorized) | ||
137 | { | ||
138 | Rest.Log.WarnFormat("{0} User not authenticated", MsgId); | ||
139 | Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, | ||
140 | rdata.request.Headers.Get("Authorization")); | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | Rest.Log.ErrorFormat("{0} User authentication failed", MsgId); | ||
145 | Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, | ||
146 | rdata.request.Headers.Get("Authorization")); | ||
147 | } | ||
148 | throw (e); | ||
149 | } | ||
150 | |||
151 | // Remove the prefix and what's left are the parameters. If we don't have | ||
152 | // the parameters we need, fail the request. Parameters do NOT include | ||
153 | // any supplied query values. | ||
154 | |||
155 | if (rdata.Parameters.Length > 0) | ||
156 | { | ||
157 | switch (rdata.method) | ||
158 | { | ||
159 | case "get" : | ||
160 | DoGet(rdata); | ||
161 | break; | ||
162 | case "put" : | ||
163 | DoPut(rdata); | ||
164 | break; | ||
165 | case "post" : | ||
166 | DoPost(rdata); | ||
167 | break; | ||
168 | case "delete" : | ||
169 | DoDelete(rdata); | ||
170 | break; | ||
171 | default : | ||
172 | Rest.Log.WarnFormat("{0} File: Method not supported: {1}", | ||
173 | MsgId, rdata.method); | ||
174 | rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method)); | ||
175 | break; | ||
176 | } | ||
177 | } | ||
178 | else | ||
179 | { | ||
180 | Rest.Log.WarnFormat("{0} File: No agent information provided", MsgId); | ||
181 | rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided"); | ||
182 | } | ||
183 | |||
184 | Rest.Log.DebugFormat("{0} REST File handler EXIT", MsgId); | ||
185 | |||
186 | } | ||
187 | |||
188 | #endregion Interface | ||
189 | |||
190 | /// <summary> | ||
191 | /// The only parameter we recognize is a UUID.If an asset with this identification is | ||
192 | /// found, it's content, base-64 encoded, is returned to the client. | ||
193 | /// </summary> | ||
194 | |||
195 | private void DoGet(FileRequestData rdata) | ||
196 | { | ||
197 | |||
198 | string path = String.Empty; | ||
199 | |||
200 | Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method); | ||
201 | |||
202 | if (rdata.Parameters.Length > 1) | ||
203 | { | ||
204 | try | ||
205 | { | ||
206 | path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2); | ||
207 | if (File.Exists(path)) | ||
208 | { | ||
209 | Rest.Log.DebugFormat("{0} File located <{1}>", MsgId, path); | ||
210 | Byte[] data = File.ReadAllBytes(path); | ||
211 | rdata.initXmlWriter(); | ||
212 | rdata.writer.WriteStartElement(String.Empty,"File",String.Empty); | ||
213 | rdata.writer.WriteAttributeString("name", path); | ||
214 | rdata.writer.WriteBase64(data,0,data.Length); | ||
215 | rdata.writer.WriteFullEndElement(); | ||
216 | } | ||
217 | else | ||
218 | { | ||
219 | Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, path); | ||
220 | rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0}", path)); | ||
221 | } | ||
222 | } | ||
223 | catch (Exception e) | ||
224 | { | ||
225 | Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, e.Message); | ||
226 | rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}", | ||
227 | path, e.Message)); | ||
228 | } | ||
229 | } | ||
230 | |||
231 | rdata.Complete(); | ||
232 | rdata.Respond(String.Format("File <{0}> : Normal completion", rdata.method)); | ||
233 | |||
234 | } | ||
235 | |||
236 | /// <summary> | ||
237 | /// UPDATE existing item, if it exists. URI identifies the item in question. | ||
238 | /// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded) | ||
239 | /// is decoded and stored in the database, identified by the supplied UUID. | ||
240 | /// </summary> | ||
241 | private void DoPut(FileRequestData rdata) | ||
242 | { | ||
243 | bool modified = false; | ||
244 | bool created = false; | ||
245 | string path = String.Empty; | ||
246 | |||
247 | Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method); | ||
248 | |||
249 | if (rdata.Parameters.Length > 1) | ||
250 | { | ||
251 | try | ||
252 | { | ||
253 | path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2); | ||
254 | bool maymod = File.Exists(path); | ||
255 | |||
256 | rdata.initXmlReader(); | ||
257 | XmlReader xml = rdata.reader; | ||
258 | |||
259 | if (!xml.ReadToFollowing("File")) | ||
260 | { | ||
261 | Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path); | ||
262 | rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data"); | ||
263 | } | ||
264 | |||
265 | Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", "")); | ||
266 | |||
267 | File.WriteAllBytes(path,data); | ||
268 | modified = maymod; | ||
269 | created = ! maymod; | ||
270 | } | ||
271 | catch (Exception e) | ||
272 | { | ||
273 | Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId, | ||
274 | e.Message); | ||
275 | } | ||
276 | } | ||
277 | else | ||
278 | { | ||
279 | Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path); | ||
280 | rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters"); | ||
281 | } | ||
282 | |||
283 | if (created) | ||
284 | { | ||
285 | rdata.appendStatus(String.Format("<p> Created file {0} <p>", path)); | ||
286 | rdata.Complete(Rest.HttpStatusCodeCreated); | ||
287 | } | ||
288 | else | ||
289 | { | ||
290 | if (modified) | ||
291 | { | ||
292 | rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path)); | ||
293 | rdata.Complete(Rest.HttpStatusCodeOK); | ||
294 | } | ||
295 | else | ||
296 | { | ||
297 | rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | rdata.Respond(String.Format("File {0} : Normal completion", rdata.method)); | ||
302 | |||
303 | } | ||
304 | |||
305 | /// <summary> | ||
306 | /// CREATE new item, replace if it exists. URI identifies the context for the item in question. | ||
307 | /// No parameters are required for POST, just thepayload. | ||
308 | /// </summary> | ||
309 | |||
310 | private void DoPost(FileRequestData rdata) | ||
311 | { | ||
312 | |||
313 | bool modified = false; | ||
314 | bool created = false; | ||
315 | string path = String.Empty; | ||
316 | |||
317 | Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method); | ||
318 | |||
319 | if (rdata.Parameters.Length > 1) | ||
320 | { | ||
321 | try | ||
322 | { | ||
323 | path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2); | ||
324 | bool maymod = File.Exists(path); | ||
325 | |||
326 | rdata.initXmlReader(); | ||
327 | XmlReader xml = rdata.reader; | ||
328 | |||
329 | if (!xml.ReadToFollowing("File")) | ||
330 | { | ||
331 | Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path); | ||
332 | rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data"); | ||
333 | } | ||
334 | |||
335 | Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", "")); | ||
336 | |||
337 | File.WriteAllBytes(path,data); | ||
338 | modified = maymod; | ||
339 | created = ! maymod; | ||
340 | } | ||
341 | catch (Exception e) | ||
342 | { | ||
343 | Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId, | ||
344 | e.Message); | ||
345 | } | ||
346 | } | ||
347 | else | ||
348 | { | ||
349 | Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path); | ||
350 | rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters"); | ||
351 | } | ||
352 | |||
353 | if (created) | ||
354 | { | ||
355 | rdata.appendStatus(String.Format("<p> Created file {0} <p>", path)); | ||
356 | rdata.Complete(Rest.HttpStatusCodeCreated); | ||
357 | } | ||
358 | else | ||
359 | { | ||
360 | if (modified) | ||
361 | { | ||
362 | rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path)); | ||
363 | rdata.Complete(Rest.HttpStatusCodeOK); | ||
364 | } | ||
365 | else | ||
366 | { | ||
367 | rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | rdata.Respond(String.Format("File {0} : Normal completion", rdata.method)); | ||
372 | |||
373 | } | ||
374 | |||
375 | /// <summary> | ||
376 | /// CREATE new item, replace if it exists. URI identifies the context for the item in question. | ||
377 | /// No parameters are required for POST, just thepayload. | ||
378 | /// </summary> | ||
379 | |||
380 | private void DoDelete(FileRequestData rdata) | ||
381 | { | ||
382 | |||
383 | bool modified = false; | ||
384 | bool created = false; | ||
385 | string path = String.Empty; | ||
386 | |||
387 | Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method); | ||
388 | |||
389 | if (rdata.Parameters.Length > 1) | ||
390 | { | ||
391 | try | ||
392 | { | ||
393 | path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2); | ||
394 | |||
395 | if (File.Exists(path)) | ||
396 | { | ||
397 | File.Delete(path); | ||
398 | } | ||
399 | } | ||
400 | catch (Exception e) | ||
401 | { | ||
402 | Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId, | ||
403 | e.Message); | ||
404 | rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}", | ||
405 | path, e.Message)); | ||
406 | } | ||
407 | } | ||
408 | else | ||
409 | { | ||
410 | Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path); | ||
411 | rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters"); | ||
412 | } | ||
413 | |||
414 | if (created) | ||
415 | { | ||
416 | rdata.appendStatus(String.Format("<p> Created file {0} <p>", path)); | ||
417 | rdata.Complete(Rest.HttpStatusCodeCreated); | ||
418 | } | ||
419 | else | ||
420 | { | ||
421 | if (modified) | ||
422 | { | ||
423 | rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path)); | ||
424 | rdata.Complete(Rest.HttpStatusCodeOK); | ||
425 | } | ||
426 | else | ||
427 | { | ||
428 | rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | rdata.Respond(String.Format("File {0} : Normal completion", rdata.method)); | ||
433 | |||
434 | } | ||
435 | |||
436 | /// <summary> | ||
437 | /// File processing has no special data area requirements. | ||
438 | /// </summary> | ||
439 | |||
440 | internal class FileRequestData : RequestData | ||
441 | { | ||
442 | internal FileRequestData(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
443 | : base(request, response, prefix) | ||
444 | { | ||
445 | } | ||
446 | } | ||
447 | } | ||
448 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs deleted file mode 100644 index 072bd6f..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs +++ /dev/null | |||
@@ -1,662 +0,0 @@ | |||
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.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using OpenSim.Framework.Servers; | ||
32 | using OpenSim.Framework.Servers.HttpServer; | ||
33 | |||
34 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
35 | { | ||
36 | /// <remarks> | ||
37 | /// The class signature reveals the roles that RestHandler plays. | ||
38 | /// | ||
39 | /// [1] It is a sub-class of RestPlugin. It inherits and extends | ||
40 | /// the functionality of this class, constraining it to the | ||
41 | /// specific needs of this REST implementation. This relates | ||
42 | /// to the plug-in mechanism supported by OpenSim, the specifics | ||
43 | /// of which are mostly hidden by RestPlugin. | ||
44 | /// [2] IRestHandler describes the interface that this class | ||
45 | /// exports to service implementations. This is the services | ||
46 | /// management interface. | ||
47 | /// [3] IHttpAgentHandler describes the interface that is exported | ||
48 | /// to the BaseHttpServer in support of this particular HTTP | ||
49 | /// processing model. This is the request interface of the | ||
50 | /// handler. | ||
51 | /// </remarks> | ||
52 | |||
53 | public class RestHandler : RestPlugin, IRestHandler, IHttpAgentHandler | ||
54 | { | ||
55 | // Handler tables: both stream and REST are supported. The path handlers and their | ||
56 | // respective allocators are stored in separate tables. | ||
57 | |||
58 | internal Dictionary<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>(); | ||
59 | internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>(); | ||
60 | internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>(); | ||
61 | |||
62 | #region local static state | ||
63 | |||
64 | private static bool handlersLoaded = false; | ||
65 | private static List<Type> classes = new List<Type>(); | ||
66 | private static List<IRest> handlers = new List<IRest>(); | ||
67 | private static Type[] parms = new Type[0]; | ||
68 | private static Object[] args = new Object[0]; | ||
69 | |||
70 | /// <summary> | ||
71 | /// This static initializer scans the ASSEMBLY for classes that | ||
72 | /// export the IRest interface and builds a list of them. These | ||
73 | /// are later activated by the handler. To add a new handler it | ||
74 | /// is only necessary to create a new services class that implements | ||
75 | /// the IRest interface, and recompile the handler. This gives | ||
76 | /// all of the build-time flexibility of a modular approach | ||
77 | /// while not introducing yet-another module loader. Note that | ||
78 | /// multiple assembles can still be built, each with its own set | ||
79 | /// of handlers. Examples of services classes are RestInventoryServices | ||
80 | /// and RestSkeleton. | ||
81 | /// </summary> | ||
82 | |||
83 | static RestHandler() | ||
84 | { | ||
85 | Module[] mods = Assembly.GetExecutingAssembly().GetModules(); | ||
86 | |||
87 | foreach (Module m in mods) | ||
88 | { | ||
89 | Type[] types = m.GetTypes(); | ||
90 | foreach (Type t in types) | ||
91 | { | ||
92 | try | ||
93 | { | ||
94 | if (t.GetInterface("IRest") != null) | ||
95 | { | ||
96 | classes.Add(t); | ||
97 | } | ||
98 | } | ||
99 | catch (Exception) | ||
100 | { | ||
101 | Rest.Log.WarnFormat("[STATIC-HANDLER]: #0 Error scanning {1}", t); | ||
102 | Rest.Log.InfoFormat("[STATIC-HANDLER]: #0 {1} is not included", t); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | #endregion local static state | ||
109 | |||
110 | #region local instance state | ||
111 | |||
112 | /// <summary> | ||
113 | /// This routine loads all of the handlers discovered during | ||
114 | /// instance initialization. | ||
115 | /// A table of all loaded and successfully constructed handlers | ||
116 | /// is built, and this table is then used by the constructor to | ||
117 | /// initialize each of the handlers in turn. | ||
118 | /// NOTE: The loading process does not automatically imply that | ||
119 | /// the handler has registered any kind of an interface, that | ||
120 | /// may be (optionally) done by the handler either during | ||
121 | /// construction, or during initialization. | ||
122 | /// | ||
123 | /// I was not able to make this code work within a constructor | ||
124 | /// so it is isolated within this method. | ||
125 | /// </summary> | ||
126 | |||
127 | private void LoadHandlers() | ||
128 | { | ||
129 | lock (handlers) | ||
130 | { | ||
131 | if (!handlersLoaded) | ||
132 | { | ||
133 | ConstructorInfo ci; | ||
134 | Object ht; | ||
135 | |||
136 | foreach (Type t in classes) | ||
137 | { | ||
138 | try | ||
139 | { | ||
140 | ci = t.GetConstructor(parms); | ||
141 | ht = ci.Invoke(args); | ||
142 | handlers.Add((IRest)ht); | ||
143 | } | ||
144 | catch (Exception e) | ||
145 | { | ||
146 | Rest.Log.WarnFormat("{0} Unable to load {1} : {2}", MsgId, t, e.Message); | ||
147 | } | ||
148 | } | ||
149 | handlersLoaded = true; | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | |||
154 | #endregion local instance state | ||
155 | |||
156 | #region overriding properties | ||
157 | |||
158 | // These properties override definitions | ||
159 | // in the base class. | ||
160 | |||
161 | // Name is used to differentiate the message header. | ||
162 | |||
163 | public override string Name | ||
164 | { | ||
165 | get { return "HANDLER"; } | ||
166 | } | ||
167 | |||
168 | // Used to partition the .ini configuration space. | ||
169 | |||
170 | public override string ConfigName | ||
171 | { | ||
172 | get { return "RestHandler"; } | ||
173 | } | ||
174 | |||
175 | // We have to rename these because we want | ||
176 | // to be able to share the values with other | ||
177 | // classes in our assembly and the base | ||
178 | // names are protected. | ||
179 | |||
180 | public string MsgId | ||
181 | { | ||
182 | get { return base.MsgID; } | ||
183 | } | ||
184 | |||
185 | public string RequestId | ||
186 | { | ||
187 | get { return base.RequestID; } | ||
188 | } | ||
189 | |||
190 | #endregion overriding properties | ||
191 | |||
192 | #region overriding methods | ||
193 | |||
194 | /// <summary> | ||
195 | /// This method is called by OpenSimMain immediately after loading the | ||
196 | /// plugin and after basic server setup, but before running any server commands. | ||
197 | /// </summary> | ||
198 | /// <remarks> | ||
199 | /// Note that entries MUST be added to the active configuration files before | ||
200 | /// the plugin can be enabled. | ||
201 | /// </remarks> | ||
202 | |||
203 | public override void Initialise(OpenSimBase openSim) | ||
204 | { | ||
205 | try | ||
206 | { | ||
207 | // This plugin will only be enabled if the broader | ||
208 | // REST plugin mechanism is enabled. | ||
209 | |||
210 | //Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId); | ||
211 | |||
212 | base.Initialise(openSim); | ||
213 | |||
214 | // IsEnabled is implemented by the base class and | ||
215 | // reflects an overall RestPlugin status | ||
216 | |||
217 | if (!IsEnabled) | ||
218 | { | ||
219 | //Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | Rest.Log.InfoFormat("{0} Rest <{1}> plugin will be enabled", MsgId, Name); | ||
224 | Rest.Log.InfoFormat("{0} Configuration parameters read from <{1}>", MsgId, ConfigName); | ||
225 | |||
226 | // These are stored in static variables to make | ||
227 | // them easy to reach from anywhere in the assembly. | ||
228 | |||
229 | Rest.main = openSim; | ||
230 | if (Rest.main == null) | ||
231 | throw new Exception("OpenSim base pointer is null"); | ||
232 | |||
233 | Rest.Plugin = this; | ||
234 | Rest.Config = Config; | ||
235 | Rest.Prefix = Prefix; | ||
236 | Rest.GodKey = GodKey; | ||
237 | Rest.Authenticate = Rest.Config.GetBoolean("authenticate", Rest.Authenticate); | ||
238 | Rest.Scheme = Rest.Config.GetString("auth-scheme", Rest.Scheme); | ||
239 | Rest.Secure = Rest.Config.GetBoolean("secured", Rest.Secure); | ||
240 | Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape", Rest.ExtendedEscape); | ||
241 | Rest.Realm = Rest.Config.GetString("realm", Rest.Realm); | ||
242 | Rest.DumpAsset = Rest.Config.GetBoolean("dump-asset", Rest.DumpAsset); | ||
243 | Rest.Fill = Rest.Config.GetBoolean("path-fill", Rest.Fill); | ||
244 | Rest.DumpLineSize = Rest.Config.GetInt("dump-line-size", Rest.DumpLineSize); | ||
245 | Rest.FlushEnabled = Rest.Config.GetBoolean("flush-on-error", Rest.FlushEnabled); | ||
246 | |||
247 | // Note: Odd spacing is required in the following strings | ||
248 | |||
249 | Rest.Log.InfoFormat("{0} Authentication is {1}required", MsgId, | ||
250 | (Rest.Authenticate ? "" : "not ")); | ||
251 | |||
252 | Rest.Log.InfoFormat("{0} Security is {1}enabled", MsgId, | ||
253 | (Rest.Secure ? "" : "not ")); | ||
254 | |||
255 | Rest.Log.InfoFormat("{0} Extended URI escape processing is {1}enabled", MsgId, | ||
256 | (Rest.ExtendedEscape ? "" : "not ")); | ||
257 | |||
258 | Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId, | ||
259 | (Rest.DumpAsset ? "" : "not ")); | ||
260 | |||
261 | // The supplied prefix MUST be absolute | ||
262 | |||
263 | if (Rest.Prefix.Substring(0,1) != Rest.UrlPathSeparator) | ||
264 | { | ||
265 | Rest.Log.WarnFormat("{0} Prefix <{1}> is not absolute and must be", MsgId, Rest.Prefix); | ||
266 | Rest.Log.InfoFormat("{0} Prefix changed to </{1}>", MsgId, Rest.Prefix); | ||
267 | Rest.Prefix = String.Format("{0}{1}", Rest.UrlPathSeparator, Rest.Prefix); | ||
268 | } | ||
269 | |||
270 | // If data dumping is requested, report on the chosen line | ||
271 | // length. | ||
272 | |||
273 | if (Rest.DumpAsset) | ||
274 | { | ||
275 | Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId, Rest.DumpLineSize); | ||
276 | } | ||
277 | |||
278 | // Load all of the handlers present in the | ||
279 | // assembly | ||
280 | |||
281 | // In principle, as we're an application plug-in, | ||
282 | // most of what needs to be done could be done using | ||
283 | // static resources, however the Open Sim plug-in | ||
284 | // model makes this an instance, so that's what we | ||
285 | // need to be. | ||
286 | // There is only one Communications manager per | ||
287 | // server, and by inference, only one each of the | ||
288 | // user, asset, and inventory servers. So we can cache | ||
289 | // those using a static initializer. | ||
290 | // We move all of this processing off to another | ||
291 | // services class to minimize overlap between function | ||
292 | // and infrastructure. | ||
293 | |||
294 | LoadHandlers(); | ||
295 | |||
296 | // The intention of a post construction initializer | ||
297 | // is to allow for setup that is dependent upon other | ||
298 | // activities outside of the agency. | ||
299 | |||
300 | foreach (IRest handler in handlers) | ||
301 | { | ||
302 | try | ||
303 | { | ||
304 | handler.Initialize(); | ||
305 | } | ||
306 | catch (Exception e) | ||
307 | { | ||
308 | Rest.Log.ErrorFormat("{0} initialization error: {1}", MsgId, e.Message); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | // Now that everything is setup we can proceed to | ||
313 | // add THIS agent to the HTTP server's handler list | ||
314 | |||
315 | // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will | ||
316 | // have to be handled through the AddHttpHandler interface. | ||
317 | // if (!AddAgentHandler(Rest.Name,this)) | ||
318 | // { | ||
319 | // Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId); | ||
320 | // foreach (IRest handler in handlers) | ||
321 | // { | ||
322 | // handler.Close(); | ||
323 | // } | ||
324 | // } | ||
325 | |||
326 | } | ||
327 | catch (Exception e) | ||
328 | { | ||
329 | Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | /// <summary> | ||
334 | /// In the interests of efficiency, and because we cannot determine whether | ||
335 | /// or not this instance will actually be harvested, we clobber the only | ||
336 | /// anchoring reference to the working state for this plug-in. What the | ||
337 | /// call to close does is irrelevant to this class beyond knowing that it | ||
338 | /// can nullify the reference when it returns. | ||
339 | /// To make sure everything is copacetic we make sure the primary interface | ||
340 | /// is disabled by deleting the handler from the HTTP server tables. | ||
341 | /// </summary> | ||
342 | |||
343 | public override void Close() | ||
344 | { | ||
345 | Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId); | ||
346 | |||
347 | // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will | ||
348 | // have to be handled through the AddHttpHandler interface. | ||
349 | // try | ||
350 | // { | ||
351 | // RemoveAgentHandler(Rest.Name, this); | ||
352 | // } | ||
353 | // catch (KeyNotFoundException){} | ||
354 | |||
355 | foreach (IRest handler in handlers) | ||
356 | { | ||
357 | handler.Close(); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | #endregion overriding methods | ||
362 | |||
363 | #region interface methods | ||
364 | |||
365 | /// <summary> | ||
366 | /// This method is called by the HTTP server to match an incoming | ||
367 | /// request. It scans all of the strings registered by the | ||
368 | /// underlying handlers and looks for the best match. It returns | ||
369 | /// true if a match is found. | ||
370 | /// The matching process could be made arbitrarily complex. | ||
371 | /// Note: The match is case-insensitive. | ||
372 | /// </summary> | ||
373 | |||
374 | public bool Match(OSHttpRequest request, OSHttpResponse response) | ||
375 | { | ||
376 | |||
377 | string path = request.RawUrl.ToLower(); | ||
378 | |||
379 | // Rest.Log.DebugFormat("{0} Match ENTRY", MsgId); | ||
380 | |||
381 | try | ||
382 | { | ||
383 | foreach (string key in pathHandlers.Keys) | ||
384 | { | ||
385 | // Rest.Log.DebugFormat("{0} Match testing {1} against agent prefix <{2}>", MsgId, path, key); | ||
386 | |||
387 | // Note that Match will not necessarily find the handler that will | ||
388 | // actually be used - it does no test for the "closest" fit. It | ||
389 | // simply reflects that at least one possible handler exists. | ||
390 | |||
391 | if (path.StartsWith(key)) | ||
392 | { | ||
393 | // Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key); | ||
394 | |||
395 | // This apparently odd evaluation is needed to prevent a match | ||
396 | // on anything other than a URI token boundary. Otherwise we | ||
397 | // may match on URL's that were not intended for this handler. | ||
398 | |||
399 | return (path.Length == key.Length || | ||
400 | path.Substring(key.Length, 1) == Rest.UrlPathSeparator); | ||
401 | } | ||
402 | } | ||
403 | |||
404 | path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path); | ||
405 | |||
406 | foreach (string key in streamHandlers.Keys) | ||
407 | { | ||
408 | // Rest.Log.DebugFormat("{0} Match testing {1} against stream prefix <{2}>", MsgId, path, key); | ||
409 | |||
410 | // Note that Match will not necessarily find the handler that will | ||
411 | // actually be used - it does no test for the "closest" fit. It | ||
412 | // simply reflects that at least one possible handler exists. | ||
413 | |||
414 | if (path.StartsWith(key)) | ||
415 | { | ||
416 | // Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key); | ||
417 | |||
418 | // This apparently odd evaluation is needed to prevent a match | ||
419 | // on anything other than a URI token boundary. Otherwise we | ||
420 | // may match on URL's that were not intended for this handler. | ||
421 | |||
422 | return (path.Length == key.Length || | ||
423 | path.Substring(key.Length, 1) == Rest.UrlPathSeparator); | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | catch (Exception e) | ||
428 | { | ||
429 | Rest.Log.ErrorFormat("{0} matching exception for path <{1}> : {2}", MsgId, path, e.Message); | ||
430 | } | ||
431 | |||
432 | return false; | ||
433 | } | ||
434 | |||
435 | /// <summary> | ||
436 | /// This is called by the HTTP server once the handler has indicated | ||
437 | /// that it is able to handle the request. | ||
438 | /// Preconditions: | ||
439 | /// [1] request != null and is a valid request object | ||
440 | /// [2] response != null and is a valid response object | ||
441 | /// Behavior is undefined if preconditions are not satisfied. | ||
442 | /// </summary> | ||
443 | |||
444 | public bool Handle(OSHttpRequest request, OSHttpResponse response) | ||
445 | { | ||
446 | bool handled; | ||
447 | base.MsgID = base.RequestID; | ||
448 | |||
449 | // Debug only | ||
450 | |||
451 | if (Rest.DEBUG) | ||
452 | { | ||
453 | Rest.Log.DebugFormat("{0} ENTRY", MsgId); | ||
454 | Rest.Log.DebugFormat("{0} Agent: {1}", MsgId, request.UserAgent); | ||
455 | Rest.Log.DebugFormat("{0} Method: {1}", MsgId, request.HttpMethod); | ||
456 | |||
457 | for (int i = 0; i < request.Headers.Count; i++) | ||
458 | { | ||
459 | Rest.Log.DebugFormat("{0} Header [{1}] : <{2}> = <{3}>", | ||
460 | MsgId, i, request.Headers.GetKey(i), request.Headers.Get(i)); | ||
461 | } | ||
462 | Rest.Log.DebugFormat("{0} URI: {1}", MsgId, request.RawUrl); | ||
463 | } | ||
464 | |||
465 | // If a path handler worked we're done, otherwise try any | ||
466 | // available stream handlers too. | ||
467 | |||
468 | try | ||
469 | { | ||
470 | handled = (FindPathHandler(request, response) || | ||
471 | FindStreamHandler(request, response)); | ||
472 | } | ||
473 | catch (Exception e) | ||
474 | { | ||
475 | // A raw exception indicates that something we weren't expecting has | ||
476 | // happened. This should always reflect a shortcoming in the plugin, | ||
477 | // or a failure to satisfy the preconditions. It should not reflect | ||
478 | // an error in the request itself. Under such circumstances the state | ||
479 | // of the request cannot be determined and we are obliged to mark it | ||
480 | // as 'handled'. | ||
481 | |||
482 | Rest.Log.ErrorFormat("{0} Plugin error: {1}", MsgId, e.Message); | ||
483 | handled = true; | ||
484 | } | ||
485 | |||
486 | Rest.Log.DebugFormat("{0} EXIT", MsgId); | ||
487 | |||
488 | return handled; | ||
489 | } | ||
490 | |||
491 | #endregion interface methods | ||
492 | |||
493 | /// <summary> | ||
494 | /// If there is a stream handler registered that can handle the | ||
495 | /// request, then fine. If the request is not matched, do | ||
496 | /// nothing. | ||
497 | /// Note: The selection is case-insensitive | ||
498 | /// </summary> | ||
499 | |||
500 | private bool FindStreamHandler(OSHttpRequest request, OSHttpResponse response) | ||
501 | { | ||
502 | RequestData rdata = new RequestData(request, response, String.Empty); | ||
503 | |||
504 | string bestMatch = String.Empty; | ||
505 | string path = String.Format("{0}:{1}", rdata.method, rdata.path).ToLower(); | ||
506 | |||
507 | Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path); | ||
508 | |||
509 | if (!IsEnabled) | ||
510 | { | ||
511 | return false; | ||
512 | } | ||
513 | |||
514 | foreach (string pattern in streamHandlers.Keys) | ||
515 | { | ||
516 | if (path.StartsWith(pattern)) | ||
517 | { | ||
518 | if (pattern.Length > bestMatch.Length) | ||
519 | { | ||
520 | bestMatch = pattern; | ||
521 | } | ||
522 | } | ||
523 | } | ||
524 | |||
525 | // Handle using the best match available | ||
526 | |||
527 | if (bestMatch.Length > 0) | ||
528 | { | ||
529 | Rest.Log.DebugFormat("{0} Stream-based handler matched with <{1}>", MsgId, bestMatch); | ||
530 | RestStreamHandler handler = streamHandlers[bestMatch]; | ||
531 | rdata.buffer = handler.Handle(rdata.path, rdata.request.InputStream, rdata.request, rdata.response); | ||
532 | rdata.AddHeader(rdata.response.ContentType,handler.ContentType); | ||
533 | rdata.Respond("FindStreamHandler Completion"); | ||
534 | } | ||
535 | |||
536 | return rdata.handled; | ||
537 | } | ||
538 | |||
539 | /// <summary> | ||
540 | /// Add a stream handler for the designated HTTP method and path prefix. | ||
541 | /// If the handler is not enabled, the request is ignored. If the path | ||
542 | /// does not start with the REST prefix, it is added. If method-qualified | ||
543 | /// path has not already been registered, the method is added to the active | ||
544 | /// handler table. | ||
545 | /// </summary> | ||
546 | public void AddStreamHandler(string httpMethod, string path, RestMethod method) | ||
547 | { | ||
548 | if (!IsEnabled) | ||
549 | { | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | if (!path.StartsWith(Rest.Prefix)) | ||
554 | { | ||
555 | path = String.Format("{0}{1}", Rest.Prefix, path); | ||
556 | } | ||
557 | |||
558 | path = String.Format("{0}{1}{2}", httpMethod, Rest.UrlMethodSeparator, path); | ||
559 | |||
560 | // Conditionally add to the list | ||
561 | |||
562 | if (!streamHandlers.ContainsKey(path)) | ||
563 | { | ||
564 | streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method)); | ||
565 | Rest.Log.DebugFormat("{0} Added handler for {1}", MsgId, path); | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path); | ||
570 | } | ||
571 | } | ||
572 | |||
573 | /// <summary> | ||
574 | /// Given the supplied request/response, if the handler is enabled, the inbound | ||
575 | /// information is used to match an entry in the active path handler tables, using | ||
576 | /// the method-qualified path information. If a match is found, then the handler is | ||
577 | /// invoked. The result is the boolean result of the handler, or false if no | ||
578 | /// handler was located. The boolean indicates whether or not the request has been | ||
579 | /// handled, not whether or not the request was successful - that information is in | ||
580 | /// the response. | ||
581 | /// Note: The selection process is case-insensitive | ||
582 | /// </summary> | ||
583 | |||
584 | internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response) | ||
585 | { | ||
586 | RequestData rdata = null; | ||
587 | string bestMatch = null; | ||
588 | |||
589 | if (!IsEnabled) | ||
590 | { | ||
591 | return false; | ||
592 | } | ||
593 | |||
594 | // Conditionally add to the list | ||
595 | |||
596 | Rest.Log.DebugFormat("{0} Checking for path handler for <{1}>", MsgId, request.RawUrl); | ||
597 | |||
598 | foreach (string pattern in pathHandlers.Keys) | ||
599 | { | ||
600 | if (request.RawUrl.ToLower().StartsWith(pattern)) | ||
601 | { | ||
602 | if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) | ||
603 | { | ||
604 | bestMatch = pattern; | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | |||
609 | if (!String.IsNullOrEmpty(bestMatch)) | ||
610 | { | ||
611 | rdata = pathAllocators[bestMatch](request, response, bestMatch); | ||
612 | |||
613 | Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch); | ||
614 | |||
615 | try | ||
616 | { | ||
617 | pathHandlers[bestMatch](rdata); | ||
618 | } | ||
619 | |||
620 | // A plugin generated error indicates a request-related error | ||
621 | // that has been handled by the plugin. | ||
622 | |||
623 | catch (RestException r) | ||
624 | { | ||
625 | Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message); | ||
626 | } | ||
627 | } | ||
628 | |||
629 | return (rdata == null) ? false : rdata.handled; | ||
630 | } | ||
631 | |||
632 | /// <summary> | ||
633 | /// A method handler and a request allocator are stored using the designated | ||
634 | /// path as a key. If an entry already exists, it is replaced by the new one. | ||
635 | /// </summary> | ||
636 | |||
637 | public void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra) | ||
638 | { | ||
639 | if (!IsEnabled) | ||
640 | { | ||
641 | return; | ||
642 | } | ||
643 | |||
644 | if (pathHandlers.ContainsKey(path)) | ||
645 | { | ||
646 | Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path); | ||
647 | pathHandlers.Remove(path); | ||
648 | } | ||
649 | |||
650 | if (pathAllocators.ContainsKey(path)) | ||
651 | { | ||
652 | Rest.Log.DebugFormat("{0} Replacing allocator for <${1}>", MsgId, path); | ||
653 | pathAllocators.Remove(path); | ||
654 | } | ||
655 | |||
656 | Rest.Log.DebugFormat("{0} Adding path handler for {1}", MsgId, path); | ||
657 | |||
658 | pathHandlers.Add(path, mh); | ||
659 | pathAllocators.Add(path, ra); | ||
660 | } | ||
661 | } | ||
662 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs deleted file mode 100644 index 536f167..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs +++ /dev/null | |||
@@ -1,2343 +0,0 @@ | |||
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.Collections.Generic; | ||
30 | using System.Drawing; | ||
31 | using System.Globalization; | ||
32 | using System.IO; | ||
33 | using System.Threading; | ||
34 | using System.Timers; | ||
35 | using System.Xml; | ||
36 | using OpenMetaverse; | ||
37 | using OpenMetaverse.Imaging; | ||
38 | using OpenSim.Framework; | ||
39 | |||
40 | using OpenSim.Framework.Servers; | ||
41 | using OpenSim.Framework.Servers.HttpServer; | ||
42 | using Timer=System.Timers.Timer; | ||
43 | |||
44 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
45 | { | ||
46 | public class RestInventoryServices : IRest | ||
47 | { | ||
48 | // private static readonly int PARM_USERID = 0; | ||
49 | // private static readonly int PARM_PATH = 1; | ||
50 | |||
51 | // private bool enabled = false; | ||
52 | private string qPrefix = "inventory"; | ||
53 | |||
54 | // private static readonly string PRIVATE_ROOT_NAME = "My Inventory"; | ||
55 | |||
56 | /// <summary> | ||
57 | /// The constructor makes sure that the service prefix is absolute | ||
58 | /// and the registers the service handler and the allocator. | ||
59 | /// </summary> | ||
60 | |||
61 | public RestInventoryServices() | ||
62 | { | ||
63 | Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId); | ||
64 | Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); | ||
65 | |||
66 | // If a relative path was specified for the handler's domain, | ||
67 | // add the standard prefix to make it absolute, e.g. /admin | ||
68 | |||
69 | if (!qPrefix.StartsWith(Rest.UrlPathSeparator)) | ||
70 | { | ||
71 | Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId); | ||
72 | qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix); | ||
73 | Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix); | ||
74 | } | ||
75 | |||
76 | // Register interface using the absolute URI. | ||
77 | |||
78 | Rest.Plugin.AddPathHandler(DoInventory,qPrefix,Allocate); | ||
79 | |||
80 | // Activate if everything went OK | ||
81 | |||
82 | // enabled = true; | ||
83 | |||
84 | Rest.Log.InfoFormat("{0} Inventory services initialization complete", MsgId); | ||
85 | } | ||
86 | |||
87 | /// <summary> | ||
88 | /// Post-construction, pre-enabled initialization opportunity | ||
89 | /// Not currently exploited. | ||
90 | /// </summary> | ||
91 | |||
92 | public void Initialize() | ||
93 | { | ||
94 | } | ||
95 | |||
96 | /// <summary> | ||
97 | /// Called by the plug-in to halt service processing. Local processing is | ||
98 | /// disabled. | ||
99 | /// </summary> | ||
100 | |||
101 | public void Close() | ||
102 | { | ||
103 | // enabled = false; | ||
104 | Rest.Log.InfoFormat("{0} Inventory services closing down", MsgId); | ||
105 | } | ||
106 | |||
107 | /// <summary> | ||
108 | /// This property is declared locally because it is used a lot and | ||
109 | /// brevity is nice. | ||
110 | /// </summary> | ||
111 | internal string MsgId | ||
112 | { | ||
113 | get { return Rest.MsgId; } | ||
114 | } | ||
115 | |||
116 | #region Interface | ||
117 | |||
118 | /// <summary> | ||
119 | /// The plugin (RestHandler) calls this method to allocate the request | ||
120 | /// state carrier for a new request. It is destroyed when the request | ||
121 | /// completes. All request-instance specific state is kept here. This | ||
122 | /// is registered when this service provider is registered. | ||
123 | /// </summary> | ||
124 | /// <param name=request>Inbound HTTP request information</param> | ||
125 | /// <param name=response>Outbound HTTP request information</param> | ||
126 | /// <param name=qPrefix>REST service domain prefix</param> | ||
127 | /// <returns>A RequestData instance suitable for this service</returns> | ||
128 | private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
129 | { | ||
130 | return (RequestData) new InventoryRequestData(request, response, prefix); | ||
131 | } | ||
132 | |||
133 | /// <summary> | ||
134 | /// This method is registered with the handler when this service provider | ||
135 | /// is initialized. It is called whenever the plug-in identifies this service | ||
136 | /// provider as the best match for a given request. | ||
137 | /// It handles all aspects of inventory REST processing, i.e. /admin/inventory | ||
138 | /// </summary> | ||
139 | /// <param name=hdata>A consolidated HTTP request work area</param> | ||
140 | private void DoInventory(RequestData hdata) | ||
141 | { | ||
142 | // InventoryRequestData rdata = (InventoryRequestData) hdata; | ||
143 | |||
144 | Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId); | ||
145 | |||
146 | // !!! REFACTORING PROBLEM | ||
147 | |||
148 | //// If we're disabled, do nothing. | ||
149 | |||
150 | //if (!enabled) | ||
151 | //{ | ||
152 | // return; | ||
153 | //} | ||
154 | |||
155 | //// Now that we know this is a serious attempt to | ||
156 | //// access inventory data, we should find out who | ||
157 | //// is asking, and make sure they are authorized | ||
158 | //// to do so. We need to validate the caller's | ||
159 | //// identity before revealing anything about the | ||
160 | //// status quo. Authenticate throws an exception | ||
161 | //// via Fail if no identity information is present. | ||
162 | //// | ||
163 | //// With the present HTTP server we can't use the | ||
164 | //// builtin authentication mechanisms because they | ||
165 | //// would be enforced for all in-bound requests. | ||
166 | //// Instead we look at the headers ourselves and | ||
167 | //// handle authentication directly. | ||
168 | |||
169 | //try | ||
170 | //{ | ||
171 | // if (!rdata.IsAuthenticated) | ||
172 | // { | ||
173 | // rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName)); | ||
174 | // } | ||
175 | //} | ||
176 | //catch (RestException e) | ||
177 | //{ | ||
178 | // if (e.statusCode == Rest.HttpStatusCodeNotAuthorized) | ||
179 | // { | ||
180 | // Rest.Log.WarnFormat("{0} User not authenticated", MsgId); | ||
181 | // Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization")); | ||
182 | // } | ||
183 | // else | ||
184 | // { | ||
185 | // Rest.Log.ErrorFormat("{0} User authentication failed", MsgId); | ||
186 | // Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization")); | ||
187 | // } | ||
188 | // throw (e); | ||
189 | //} | ||
190 | |||
191 | //Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName); | ||
192 | |||
193 | //// We can only get here if we are authorized | ||
194 | //// | ||
195 | //// The requestor may have specified an UUID or | ||
196 | //// a conjoined FirstName LastName string. We'll | ||
197 | //// try both. If we fail with the first, UUID, | ||
198 | //// attempt, we try the other. As an example, the | ||
199 | //// URI for a valid inventory request might be: | ||
200 | //// | ||
201 | //// http://<host>:<port>/admin/inventory/Arthur Dent | ||
202 | //// | ||
203 | //// Indicating that this is an inventory request for | ||
204 | //// an avatar named Arthur Dent. This is ALL that is | ||
205 | //// required to designate a GET for an entire | ||
206 | //// inventory. | ||
207 | //// | ||
208 | |||
209 | |||
210 | //// Do we have at least a user agent name? | ||
211 | |||
212 | //if (rdata.Parameters.Length < 1) | ||
213 | //{ | ||
214 | // Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId); | ||
215 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified"); | ||
216 | //} | ||
217 | |||
218 | //// The first parameter MUST be the agent identification, either an UUID | ||
219 | //// or a space-separated First-name Last-Name specification. We check for | ||
220 | //// an UUID first, if anyone names their character using a valid UUID | ||
221 | //// that identifies another existing avatar will cause this a problem... | ||
222 | |||
223 | //try | ||
224 | //{ | ||
225 | // rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]); | ||
226 | // Rest.Log.DebugFormat("{0} UUID supplied", MsgId); | ||
227 | // rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid); | ||
228 | //} | ||
229 | //catch | ||
230 | //{ | ||
231 | // string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE); | ||
232 | // if (names.Length == 2) | ||
233 | // { | ||
234 | // Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId); | ||
235 | // rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]); | ||
236 | // } | ||
237 | // else | ||
238 | // { | ||
239 | // Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId); | ||
240 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity"); | ||
241 | // } | ||
242 | //} | ||
243 | |||
244 | //// If the user profile is null then either the server is broken, or the | ||
245 | //// user is not known. We always assume the latter case. | ||
246 | |||
247 | //if (rdata.userProfile != null) | ||
248 | //{ | ||
249 | // Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}", | ||
250 | // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | ||
251 | //} | ||
252 | //else | ||
253 | //{ | ||
254 | // Rest.Log.WarnFormat("{0} No profile for {1}", MsgId, rdata.path); | ||
255 | // rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity"); | ||
256 | //} | ||
257 | |||
258 | //// If we get to here, then we have effectively validated the user's | ||
259 | //// identity. Now we need to get the inventory. If the server does not | ||
260 | //// have the inventory, we reject the request with an appropriate explanation. | ||
261 | //// | ||
262 | //// Note that inventory retrieval is an asynchronous event, we use the rdata | ||
263 | //// class instance as the basis for our synchronization. | ||
264 | //// | ||
265 | |||
266 | //rdata.uuid = rdata.userProfile.ID; | ||
267 | |||
268 | //if (Rest.InventoryServices.HasInventoryForUser(rdata.uuid)) | ||
269 | //{ | ||
270 | // rdata.root = Rest.InventoryServices.GetRootFolder(rdata.uuid); | ||
271 | |||
272 | // Rest.Log.DebugFormat("{0} Inventory Root retrieved for {1} {2}", | ||
273 | // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | ||
274 | |||
275 | // Rest.InventoryServices.GetUserInventory(rdata.uuid, rdata.GetUserInventory); | ||
276 | |||
277 | // Rest.Log.DebugFormat("{0} Inventory catalog requested for {1} {2}", | ||
278 | // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | ||
279 | |||
280 | // lock (rdata) | ||
281 | // { | ||
282 | // if (!rdata.HaveInventory) | ||
283 | // { | ||
284 | // rdata.startWD(1000); | ||
285 | // rdata.timeout = false; | ||
286 | // Monitor.Wait(rdata); | ||
287 | // } | ||
288 | // } | ||
289 | |||
290 | // if (rdata.timeout) | ||
291 | // { | ||
292 | // Rest.Log.WarnFormat("{0} Inventory not available for {1} {2}. No response from service.", | ||
293 | // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | ||
294 | // rdata.Fail(Rest.HttpStatusCodeServerError, "inventory server not responding"); | ||
295 | // } | ||
296 | |||
297 | // if (rdata.root == null) | ||
298 | // { | ||
299 | // Rest.Log.WarnFormat("{0} Inventory is not available [1] for agent {1} {2}", | ||
300 | // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | ||
301 | // rdata.Fail(Rest.HttpStatusCodeServerError, "inventory retrieval failed"); | ||
302 | // } | ||
303 | |||
304 | //} | ||
305 | //else | ||
306 | //{ | ||
307 | // Rest.Log.WarnFormat("{0} Inventory is not locally available for agent {1} {2}", | ||
308 | // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | ||
309 | // rdata.Fail(Rest.HttpStatusCodeNotFound, "no local inventory for user"); | ||
310 | //} | ||
311 | |||
312 | //// If we get here, then we have successfully retrieved the user's information | ||
313 | //// and inventory information is now available locally. | ||
314 | |||
315 | //switch (rdata.method) | ||
316 | //{ | ||
317 | // case Rest.HEAD : // Do the processing, set the status code, suppress entity | ||
318 | // DoGet(rdata); | ||
319 | // rdata.buffer = null; | ||
320 | // break; | ||
321 | |||
322 | // case Rest.GET : // Do the processing, set the status code, return entity | ||
323 | // DoGet(rdata); | ||
324 | // break; | ||
325 | |||
326 | // case Rest.PUT : // Update named element | ||
327 | // DoUpdate(rdata); | ||
328 | // break; | ||
329 | |||
330 | // case Rest.POST : // Add new information to identified context. | ||
331 | // DoExtend(rdata); | ||
332 | // break; | ||
333 | |||
334 | // case Rest.DELETE : // Delete information | ||
335 | // DoDelete(rdata); | ||
336 | // break; | ||
337 | |||
338 | // default : | ||
339 | // Rest.Log.WarnFormat("{0} Method {1} not supported for {2}", | ||
340 | // MsgId, rdata.method, rdata.path); | ||
341 | // rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed, | ||
342 | // String.Format("{0} not supported", rdata.method)); | ||
343 | // break; | ||
344 | //} | ||
345 | } | ||
346 | |||
347 | #endregion Interface | ||
348 | |||
349 | #region method-specific processing | ||
350 | |||
351 | /// <summary> | ||
352 | /// This method implements GET processing for inventory. | ||
353 | /// Any remaining parameters are used to locate the | ||
354 | /// corresponding subtree based upon node name. | ||
355 | /// </summary> | ||
356 | /// <param name=rdata>HTTP service request work area</param> | ||
357 | // private void DoGet(InventoryRequestData rdata) | ||
358 | // { | ||
359 | // rdata.initXmlWriter(); | ||
360 | // | ||
361 | // rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty); | ||
362 | // | ||
363 | // // If there are additional parameters, then these represent | ||
364 | // // a path relative to the root of the inventory. This path | ||
365 | // // must be traversed before we format the sub-tree thus | ||
366 | // // identified. | ||
367 | // | ||
368 | // traverse(rdata, rdata.root, PARM_PATH); | ||
369 | // | ||
370 | // // Close all open elements | ||
371 | // | ||
372 | // rdata.writer.WriteFullEndElement(); | ||
373 | // | ||
374 | // // Indicate a successful request | ||
375 | // | ||
376 | // rdata.Complete(); | ||
377 | // | ||
378 | // // Send the response to the user. The body will be implicitly | ||
379 | // // constructed from the result of the XML writer. | ||
380 | // | ||
381 | // rdata.Respond(String.Format("Inventory {0} Normal completion", rdata.method)); | ||
382 | // } | ||
383 | |||
384 | /// <summary> | ||
385 | /// In the case of the inventory, and probably in general, | ||
386 | /// the distinction between PUT and POST is not always | ||
387 | /// easy to discern. The standard is badly worded in places, | ||
388 | /// and adding a node to a hierarchy can be viewed as | ||
389 | /// an addition, or as a modification to the inventory as | ||
390 | /// a whole. This is exacerbated by an unjustified lack of | ||
391 | /// consistency across different implementations. | ||
392 | /// | ||
393 | /// For OpenSim PUT is an update and POST is an addition. This | ||
394 | /// is the behavior required by the HTTP specification and | ||
395 | /// therefore as required by REST. | ||
396 | /// | ||
397 | /// The best way to explain the distinction is to | ||
398 | /// consider the relationship between the URI and the | ||
399 | /// enclosed entity. For PUT, the URI identifies the | ||
400 | /// actual entity to be modified or replaced, i.e. the | ||
401 | /// enclosed entity. | ||
402 | /// | ||
403 | /// If the operation is POST,then the URI describes the | ||
404 | /// context into which the new entity will be added. | ||
405 | /// | ||
406 | /// As an example, suppose the URI contains: | ||
407 | /// /admin/inventory/Clothing | ||
408 | /// | ||
409 | /// A PUT request will normally result in some modification of | ||
410 | /// the folder or item named "Clothing". Whereas a POST | ||
411 | /// request will normally add some new information into the | ||
412 | /// content identified by Clothing. It follows from this | ||
413 | /// that for POST, the element identified by the URI MUST | ||
414 | /// be a folder. | ||
415 | /// </summary> | ||
416 | |||
417 | /// <summary> | ||
418 | /// POST adds new information to the inventory in the | ||
419 | /// context identified by the URI. | ||
420 | /// </summary> | ||
421 | /// <param name=rdata>HTTP service request work area</param> | ||
422 | // private void DoExtend(InventoryRequestData rdata) | ||
423 | // { | ||
424 | // bool created = false; | ||
425 | // bool modified = false; | ||
426 | // string newnode = String.Empty; | ||
427 | // | ||
428 | // // Resolve the context node specified in the URI. Entity | ||
429 | // // data will be ADDED beneath this node. rdata already contains | ||
430 | // // information about the current content of the user's | ||
431 | // // inventory. | ||
432 | // | ||
433 | // Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill); | ||
434 | // | ||
435 | // // Processing depends upon the type of inventory node | ||
436 | // // identified in the URI. This is the CONTEXT for the | ||
437 | // // change. We either got a context or we threw an | ||
438 | // // exception. | ||
439 | // | ||
440 | // // It follows that we can only add information if the URI | ||
441 | // // has identified a folder. So only a type of folder is supported | ||
442 | // // in this case. | ||
443 | // | ||
444 | // if (typeof(InventoryFolderBase) == InventoryNode.GetType() || | ||
445 | // typeof(InventoryFolderImpl) == InventoryNode.GetType()) | ||
446 | // { | ||
447 | // // Cast the context node appropriately. | ||
448 | // | ||
449 | // InventoryFolderBase context = (InventoryFolderBase) InventoryNode; | ||
450 | // | ||
451 | // Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}", | ||
452 | // MsgId, rdata.method, rdata.path); | ||
453 | // | ||
454 | // // Reconstitute the inventory sub-tree from the XML supplied in the entity. | ||
455 | // // The result is a stand-alone inventory subtree, not yet integrated into the | ||
456 | // // existing tree. An inventory collection consists of three components: | ||
457 | // // [1] A (possibly empty) set of folders. | ||
458 | // // [2] A (possibly empty) set of items. | ||
459 | // // [3] A (possibly empty) set of assets. | ||
460 | // // If all of these are empty, then the POST is a harmless no-operation. | ||
461 | // | ||
462 | // XmlInventoryCollection entity = ReconstituteEntity(rdata); | ||
463 | // | ||
464 | // // Inlined assets can be included in entity. These must be incorporated into | ||
465 | // // the asset database before we attempt to update the inventory. If anything | ||
466 | // // fails, return a failure to requestor. | ||
467 | // | ||
468 | // if (entity.Assets.Count > 0) | ||
469 | // { | ||
470 | // Rest.Log.DebugFormat("{0} Adding {1} assets to server", | ||
471 | // MsgId, entity.Assets.Count); | ||
472 | // | ||
473 | // foreach (AssetBase asset in entity.Assets) | ||
474 | // { | ||
475 | // Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", | ||
476 | // MsgId, asset.ID, asset.Type, asset.Name); | ||
477 | // Rest.AssetServices.Store(asset); | ||
478 | // | ||
479 | // created = true; | ||
480 | // rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", | ||
481 | // asset.Name, asset.ID)); | ||
482 | // | ||
483 | // if (Rest.DEBUG && Rest.DumpAsset) | ||
484 | // { | ||
485 | // Rest.Dump(asset.Data); | ||
486 | // } | ||
487 | // } | ||
488 | // } | ||
489 | // | ||
490 | // // Modify the context using the collection of folders and items | ||
491 | // // returned in the XmlInventoryCollection. | ||
492 | // | ||
493 | // foreach (InventoryFolderBase folder in entity.Folders) | ||
494 | // { | ||
495 | // InventoryFolderBase found; | ||
496 | // | ||
497 | // // If the parentID is zero, then this folder is going | ||
498 | // // into the root folder identified by the URI. The requestor | ||
499 | // // may have already set the parent ID explicitly, in which | ||
500 | // // case we don't have to do it here. | ||
501 | // | ||
502 | // if (folder.ParentID == UUID.Zero || folder.ParentID == context.ID) | ||
503 | // { | ||
504 | // if (newnode != String.Empty) | ||
505 | // { | ||
506 | // Rest.Log.DebugFormat("{0} Too many resources", MsgId); | ||
507 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "only one root entity is allowed"); | ||
508 | // } | ||
509 | // folder.ParentID = context.ID; | ||
510 | // newnode = folder.Name; | ||
511 | // } | ||
512 | // | ||
513 | // // Search the existing inventory for an existing entry. If | ||
514 | // // we have one, we need to decide if it has really changed. | ||
515 | // // It could just be present as (unnecessary) context, and we | ||
516 | // // don't want to waste time updating the database in that | ||
517 | // // case, OR, it could be being moved from another location | ||
518 | // // in which case an update is most certainly necessary. | ||
519 | // | ||
520 | // found = null; | ||
521 | // | ||
522 | // foreach (InventoryFolderBase xf in rdata.folders) | ||
523 | // { | ||
524 | // // Compare identifying attribute | ||
525 | // if (xf.ID == folder.ID) | ||
526 | // { | ||
527 | // found = xf; | ||
528 | // break; | ||
529 | // } | ||
530 | // } | ||
531 | // | ||
532 | // if (found != null && FolderHasChanged(folder,found)) | ||
533 | // { | ||
534 | // Rest.Log.DebugFormat("{0} Updating existing folder", MsgId); | ||
535 | // Rest.InventoryServices.MoveFolder(folder); | ||
536 | // | ||
537 | // modified = true; | ||
538 | // rdata.appendStatus(String.Format("<p> Created folder {0}, UUID {1} <p>", | ||
539 | // folder.Name, folder.ID)); | ||
540 | // } | ||
541 | // else | ||
542 | // { | ||
543 | // Rest.Log.DebugFormat("{0} Adding new folder", MsgId); | ||
544 | // Rest.InventoryServices.AddFolder(folder); | ||
545 | // | ||
546 | // created = true; | ||
547 | // rdata.appendStatus(String.Format("<p> Modified folder {0}, UUID {1} <p>", | ||
548 | // folder.Name, folder.ID)); | ||
549 | // } | ||
550 | // } | ||
551 | // | ||
552 | // // Now we repeat a similar process for the items included | ||
553 | // // in the entity. | ||
554 | // | ||
555 | // foreach (InventoryItemBase item in entity.Items) | ||
556 | // { | ||
557 | // InventoryItemBase found = null; | ||
558 | // | ||
559 | // // If the parentID is zero, then this is going | ||
560 | // // directly into the root identified by the URI. | ||
561 | // | ||
562 | // if (item.Folder == UUID.Zero) | ||
563 | // { | ||
564 | // item.Folder = context.ID; | ||
565 | // } | ||
566 | // | ||
567 | // // Determine whether this is a new item or a | ||
568 | // // replacement definition. | ||
569 | // | ||
570 | // foreach (InventoryItemBase xi in rdata.items) | ||
571 | // { | ||
572 | // // Compare identifying attribute | ||
573 | // if (xi.ID == item.ID) | ||
574 | // { | ||
575 | // found = xi; | ||
576 | // break; | ||
577 | // } | ||
578 | // } | ||
579 | // | ||
580 | // if (found != null && ItemHasChanged(item, found)) | ||
581 | // { | ||
582 | // Rest.Log.DebugFormat("{0} Updating item {1} {2} {3} {4} {5}", | ||
583 | // MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); | ||
584 | // Rest.InventoryServices.UpdateItem(item); | ||
585 | // modified = true; | ||
586 | // rdata.appendStatus(String.Format("<p> Modified item {0}, UUID {1} <p>", item.Name, item.ID)); | ||
587 | // } | ||
588 | // else | ||
589 | // { | ||
590 | // Rest.Log.DebugFormat("{0} Adding item {1} {2} {3} {4} {5}", | ||
591 | // MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); | ||
592 | // Rest.InventoryServices.AddItem(item); | ||
593 | // created = true; | ||
594 | // rdata.appendStatus(String.Format("<p> Created item {0}, UUID {1} <p>", item.Name, item.ID)); | ||
595 | // } | ||
596 | // } | ||
597 | // | ||
598 | // if (created) | ||
599 | // { | ||
600 | // // Must include a location header with a URI that identifies the new resource. | ||
601 | // rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}/{3}", | ||
602 | // rdata.hostname, rdata.port,rdata.path,newnode)); | ||
603 | // rdata.Complete(Rest.HttpStatusCodeCreated); | ||
604 | // } | ||
605 | // else | ||
606 | // { | ||
607 | // if (modified) | ||
608 | // { | ||
609 | // rdata.Complete(Rest.HttpStatusCodeOK); | ||
610 | // } | ||
611 | // else | ||
612 | // { | ||
613 | // rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
614 | // } | ||
615 | // } | ||
616 | // | ||
617 | // rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); | ||
618 | // } | ||
619 | // else | ||
620 | // { | ||
621 | // Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}", | ||
622 | // MsgId, rdata.method, rdata.path, InventoryNode.GetType()); | ||
623 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid resource context"); | ||
624 | // } | ||
625 | // } | ||
626 | |||
627 | /// <summary> | ||
628 | /// PUT updates the URI-identified element in the inventory. This | ||
629 | /// is actually far more flexible than it might at first sound. For | ||
630 | /// PUT the URI serves two purposes: | ||
631 | /// [1] It identifies the user whose inventory is to be | ||
632 | /// processed. | ||
633 | /// [2] It optionally specifies a subtree of the inventory | ||
634 | /// that is to be used to resolve any relative subtree | ||
635 | /// specifications in the entity. If nothing is specified | ||
636 | /// then the whole of the private inventory is implied. | ||
637 | /// Please note that the subtree specified by the URI is only relevant | ||
638 | /// to an entity containing a URI relative specification, i.e. one or | ||
639 | /// more elements do not specify parent folder information. These | ||
640 | /// elements will be implicitly referenced within the context identified | ||
641 | /// by the URI. | ||
642 | /// If an element in the entity specifies an explicit parent folder, then | ||
643 | /// that parent is effective, regardless of any value specified in the | ||
644 | /// URI. If the parent does not exist, then the element, and any dependent | ||
645 | /// elements, are ignored. This case is actually detected and handled | ||
646 | /// during the reconstitution process. | ||
647 | /// </summary> | ||
648 | /// <param name=rdata>HTTP service request work area</param> | ||
649 | // private void DoUpdate(InventoryRequestData rdata) | ||
650 | // { | ||
651 | // int count = 0; | ||
652 | // bool created = false; | ||
653 | // bool modified = false; | ||
654 | // | ||
655 | // // Resolve the inventory node that is to be modified. | ||
656 | // // rdata already contains information about the current | ||
657 | // // content of the user's inventory. | ||
658 | // | ||
659 | // Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill); | ||
660 | // | ||
661 | // // As long as we have a node, then we have something | ||
662 | // // meaningful to do, unlike POST. So we reconstitute the | ||
663 | // // subtree before doing anything else. Note that we | ||
664 | // // etiher got a valid node or we threw an exception. | ||
665 | // | ||
666 | // XmlInventoryCollection entity = ReconstituteEntity(rdata); | ||
667 | // | ||
668 | // // Incorporate any inlined assets first. Any failures | ||
669 | // // will terminate the request. | ||
670 | // | ||
671 | // if (entity.Assets.Count > 0) | ||
672 | // { | ||
673 | // Rest.Log.DebugFormat("{0} Adding {1} assets to server", | ||
674 | // MsgId, entity.Assets.Count); | ||
675 | // | ||
676 | // foreach (AssetBase asset in entity.Assets) | ||
677 | // { | ||
678 | // Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", | ||
679 | // MsgId, asset.ID, asset.Type, asset.Name); | ||
680 | // | ||
681 | // // The asset was validated during the collection process | ||
682 | // | ||
683 | // Rest.AssetServices.Store(asset); | ||
684 | // | ||
685 | // created = true; | ||
686 | // rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.ID)); | ||
687 | // | ||
688 | // if (Rest.DEBUG && Rest.DumpAsset) | ||
689 | // { | ||
690 | // Rest.Dump(asset.Data); | ||
691 | // } | ||
692 | // } | ||
693 | // } | ||
694 | // | ||
695 | // // The URI specifies either a folder or an item to be updated. | ||
696 | // // | ||
697 | // // The root node in the entity will replace the node identified | ||
698 | // // by the URI. This means the parent will remain the same, but | ||
699 | // // any or all attributes associated with the named element | ||
700 | // // will change. | ||
701 | // // | ||
702 | // // If the inventory collection contains an element with a zero | ||
703 | // // parent ID, then this is taken to be the replacement for the | ||
704 | // // named node. The collection MAY also specify an explicit | ||
705 | // // parent ID, in this case it MAY identify the same parent as | ||
706 | // // the current node, or it MAY specify a different parent, | ||
707 | // // indicating that the folder is being moved in addition to any | ||
708 | // // other modifications being made. | ||
709 | // | ||
710 | // if (typeof(InventoryFolderBase) == InventoryNode.GetType() || | ||
711 | // typeof(InventoryFolderImpl) == InventoryNode.GetType()) | ||
712 | // { | ||
713 | // bool rfound = false; | ||
714 | // InventoryFolderBase uri = (InventoryFolderBase) InventoryNode; | ||
715 | // InventoryFolderBase xml = null; | ||
716 | // | ||
717 | // // If the entity to be replaced resolved to be the root | ||
718 | // // directory itself (My Inventory), then make sure that | ||
719 | // // the supplied data include as appropriately typed and | ||
720 | // // named folder. Note that we can;t rule out the possibility | ||
721 | // // of a sub-directory being called "My Inventory", so that | ||
722 | // // is anticipated. | ||
723 | // | ||
724 | // if (uri == rdata.root) | ||
725 | // { | ||
726 | // foreach (InventoryFolderBase folder in entity.Folders) | ||
727 | // { | ||
728 | // if ((rfound = (folder.Name == PRIVATE_ROOT_NAME))) | ||
729 | // { | ||
730 | // if ((rfound = (folder.ParentID == UUID.Zero))) | ||
731 | // break; | ||
732 | // } | ||
733 | // } | ||
734 | // | ||
735 | // if (!rfound) | ||
736 | // { | ||
737 | // Rest.Log.DebugFormat("{0} {1}: Path <{2}> will result in loss of inventory", | ||
738 | // MsgId, rdata.method, rdata.path); | ||
739 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid inventory structure"); | ||
740 | // } | ||
741 | // } | ||
742 | // | ||
743 | // // Scan the set of folders in the entity collection for an | ||
744 | // // entry that matches the context folder. It is assumed that | ||
745 | // // the only reliable indicator of this is a zero UUID (using | ||
746 | // // implicit context), or the parent's UUID matches that of the | ||
747 | // // URI designated node (explicit context). We don't allow | ||
748 | // // ambiguity in this case because this is POST and we are | ||
749 | // // supposed to be modifying a specific node. | ||
750 | // // We assign any element IDs required as an economy; we don't | ||
751 | // // want to iterate over the fodler set again if it can be | ||
752 | // // helped. | ||
753 | // | ||
754 | // foreach (InventoryFolderBase folder in entity.Folders) | ||
755 | // { | ||
756 | // if (folder.ParentID == uri.ParentID || | ||
757 | // folder.ParentID == UUID.Zero) | ||
758 | // { | ||
759 | // folder.ParentID = uri.ParentID; | ||
760 | // xml = folder; | ||
761 | // count++; | ||
762 | // } | ||
763 | // } | ||
764 | // | ||
765 | // // More than one entry is ambiguous. Other folders should be | ||
766 | // // added using the POST verb. | ||
767 | // | ||
768 | // if (count > 1) | ||
769 | // { | ||
770 | // Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous", | ||
771 | // MsgId, rdata.method, rdata.path); | ||
772 | // rdata.Fail(Rest.HttpStatusCodeConflict, "context is ambiguous"); | ||
773 | // } | ||
774 | // | ||
775 | // // Exactly one entry means we ARE replacing the node | ||
776 | // // identified by the URI. So we delete the old folder | ||
777 | // // by moving it to the trash and then purging it. | ||
778 | // // We then add all of the folders and items we | ||
779 | // // included in the entity. The subtree has been | ||
780 | // // modified. | ||
781 | // | ||
782 | // if (count == 1) | ||
783 | // { | ||
784 | // InventoryFolderBase TrashCan = GetTrashCan(rdata); | ||
785 | // | ||
786 | // // All went well, so we generate a UUID is one is | ||
787 | // // needed. | ||
788 | // | ||
789 | // if (xml.ID == UUID.Zero) | ||
790 | // { | ||
791 | // xml.ID = UUID.Random(); | ||
792 | // } | ||
793 | // | ||
794 | // uri.ParentID = TrashCan.ID; | ||
795 | // Rest.InventoryServices.MoveFolder(uri); | ||
796 | // Rest.InventoryServices.PurgeFolder(TrashCan); | ||
797 | // modified = true; | ||
798 | // } | ||
799 | // | ||
800 | // // Now, regardelss of what they represent, we | ||
801 | // // integrate all of the elements in the entity. | ||
802 | // | ||
803 | // foreach (InventoryFolderBase f in entity.Folders) | ||
804 | // { | ||
805 | // rdata.appendStatus(String.Format("<p>Moving folder {0} UUID {1} <p>", f.Name, f.ID)); | ||
806 | // Rest.InventoryServices.MoveFolder(f); | ||
807 | // } | ||
808 | // | ||
809 | // foreach (InventoryItemBase it in entity.Items) | ||
810 | // { | ||
811 | // rdata.appendStatus(String.Format("<p>Storing item {0} UUID {1} <p>", it.Name, it.ID)); | ||
812 | // Rest.InventoryServices.AddItem(it); | ||
813 | // } | ||
814 | // } | ||
815 | // | ||
816 | // /// <summary> | ||
817 | // /// URI specifies an item to be updated | ||
818 | // /// </summary> | ||
819 | // /// <remarks> | ||
820 | // /// The entity must contain a single item node to be | ||
821 | // /// updated. ID and Folder ID must be correct. | ||
822 | // /// </remarks> | ||
823 | // | ||
824 | // else | ||
825 | // { | ||
826 | // InventoryItemBase uri = (InventoryItemBase) InventoryNode; | ||
827 | // InventoryItemBase xml = null; | ||
828 | // | ||
829 | // if (entity.Folders.Count != 0) | ||
830 | // { | ||
831 | // Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>", | ||
832 | // MsgId, rdata.method, rdata.path); | ||
833 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "folder is not allowed"); | ||
834 | // } | ||
835 | // | ||
836 | // if (entity.Items.Count > 1) | ||
837 | // { | ||
838 | // Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>", | ||
839 | // MsgId, rdata.method, rdata.path); | ||
840 | // rdata.Fail(Rest.HttpStatusCodeBadRequest, "too may items"); | ||
841 | // } | ||
842 | // | ||
843 | // xml = entity.Items[0]; | ||
844 | // | ||
845 | // if (xml.ID == UUID.Zero) | ||
846 | // { | ||
847 | // xml.ID = UUID.Random(); | ||
848 | // } | ||
849 | // | ||
850 | // // If the folder reference has changed, then this item is | ||
851 | // // being moved. Otherwise we'll just delete the old, and | ||
852 | // // add in the new. | ||
853 | // | ||
854 | // // Delete the old item | ||
855 | // | ||
856 | // List<UUID> uuids = new List<UUID>(); | ||
857 | // uuids.Add(uri.ID); | ||
858 | // Rest.InventoryServices.DeleteItems(uri.Owner, uuids); | ||
859 | // | ||
860 | // // Add the new item to the inventory | ||
861 | // | ||
862 | // Rest.InventoryServices.AddItem(xml); | ||
863 | // | ||
864 | // rdata.appendStatus(String.Format("<p>Storing item {0} UUID {1} <p>", xml.Name, xml.ID)); | ||
865 | // } | ||
866 | // | ||
867 | // if (created) | ||
868 | // { | ||
869 | // rdata.Complete(Rest.HttpStatusCodeCreated); | ||
870 | // } | ||
871 | // else | ||
872 | // { | ||
873 | // if (modified) | ||
874 | // { | ||
875 | // rdata.Complete(Rest.HttpStatusCodeOK); | ||
876 | // } | ||
877 | // else | ||
878 | // { | ||
879 | // rdata.Complete(Rest.HttpStatusCodeNoContent); | ||
880 | // } | ||
881 | // } | ||
882 | // | ||
883 | // rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); | ||
884 | // } | ||
885 | |||
886 | /// <summary> | ||
887 | /// Arguably the most damaging REST interface. It deletes the inventory | ||
888 | /// item or folder identified by the URI. | ||
889 | /// | ||
890 | /// We only process if the URI identified node appears to exist | ||
891 | /// We do not test for success because we either get a context, | ||
892 | /// or an exception is thrown. | ||
893 | /// | ||
894 | /// Folders are deleted by moving them to another folder and then | ||
895 | /// purging that folder. We'll do that by creating a temporary | ||
896 | /// sub-folder in the TrashCan and purging that folder's | ||
897 | /// contents. If we can't can it, we don't delete it... | ||
898 | /// So, if no trashcan is available, the request does nothing. | ||
899 | /// Items are summarily deleted. | ||
900 | /// | ||
901 | /// In the interests of safety, a delete request should normally | ||
902 | /// be performed using UUID, as a name might identify several | ||
903 | /// elements. | ||
904 | /// </summary> | ||
905 | /// <param name=rdata>HTTP service request work area</param> | ||
906 | // private void DoDelete(InventoryRequestData rdata) | ||
907 | // { | ||
908 | // Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, false); | ||
909 | // | ||
910 | // if (typeof(InventoryFolderBase) == InventoryNode.GetType() || | ||
911 | // typeof(InventoryFolderImpl) == InventoryNode.GetType()) | ||
912 | // { | ||
913 | // InventoryFolderBase TrashCan = GetTrashCan(rdata); | ||
914 | // | ||
915 | // InventoryFolderBase folder = (InventoryFolderBase) InventoryNode; | ||
916 | // Rest.Log.DebugFormat("{0} {1}: Folder {2} will be deleted", | ||
917 | // MsgId, rdata.method, rdata.path); | ||
918 | // folder.ParentID = TrashCan.ID; | ||
919 | // Rest.InventoryServices.MoveFolder(folder); | ||
920 | // Rest.InventoryServices.PurgeFolder(TrashCan); | ||
921 | // | ||
922 | // rdata.appendStatus(String.Format("<p>Deleted folder {0} UUID {1} <p>", folder.Name, folder.ID)); | ||
923 | // } | ||
924 | // | ||
925 | // // Deleting items is much more straight forward. | ||
926 | // | ||
927 | // else | ||
928 | // { | ||
929 | // InventoryItemBase item = (InventoryItemBase) InventoryNode; | ||
930 | // Rest.Log.DebugFormat("{0} {1}: Item {2} will be deleted", | ||
931 | // MsgId, rdata.method, rdata.path); | ||
932 | // List<UUID> uuids = new List<UUID>(); | ||
933 | // uuids.Add(item.ID); | ||
934 | // Rest.InventoryServices.DeleteItems(item.Owner, uuids); | ||
935 | // rdata.appendStatus(String.Format("<p>Deleted item {0} UUID {1} <p>", item.Name, item.ID)); | ||
936 | // } | ||
937 | // | ||
938 | // rdata.Complete(); | ||
939 | // rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); | ||
940 | // } | ||
941 | |||
942 | #endregion method-specific processing | ||
943 | |||
944 | /// <summary> | ||
945 | /// This method is called to obtain the OpenSim inventory object identified | ||
946 | /// by the supplied URI. This may be either an Item or a Folder, so a suitably | ||
947 | /// ambiguous return type is employed (Object). This method recurses as | ||
948 | /// necessary to process the designated hierarchy. | ||
949 | /// | ||
950 | /// If we reach the end of the URI then we return the contextual folder to | ||
951 | /// our caller. | ||
952 | /// | ||
953 | /// If we are not yet at the end of the URI we attempt to find a child folder | ||
954 | /// and if we succeed we recurse. | ||
955 | /// | ||
956 | /// If this is the last node, then we look to see if this is an item. If it is, | ||
957 | /// we return that item. | ||
958 | /// | ||
959 | /// If we reach the end of an inventory path and the URI si not yet exhausted, | ||
960 | /// then if 'fill' is specified, we create the intermediate nodes. | ||
961 | /// | ||
962 | /// Otherwise we fail the request on the ground of an invalid URI. | ||
963 | /// | ||
964 | /// An ambiguous request causes the request to fail. | ||
965 | /// | ||
966 | /// </summary> | ||
967 | /// <param name=rdata>HTTP service request work area</param> | ||
968 | /// <param name=folder>The folder to be searched (parent)</param> | ||
969 | /// <param name=pi>URI parameter index</param> | ||
970 | /// <param name=fill>Should missing path members be created?</param> | ||
971 | |||
972 | private Object getInventoryNode(InventoryRequestData rdata, | ||
973 | InventoryFolderBase folder, | ||
974 | int pi, bool fill) | ||
975 | { | ||
976 | InventoryFolderBase foundf = null; | ||
977 | int fk = 0; | ||
978 | |||
979 | Rest.Log.DebugFormat("{0} Searching folder {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi); | ||
980 | |||
981 | // We have just run off the end of the parameter sequence | ||
982 | |||
983 | if (pi >= rdata.Parameters.Length) | ||
984 | { | ||
985 | return folder; | ||
986 | } | ||
987 | |||
988 | // There are more names in the parameter sequence, | ||
989 | // look for the folder named by param[pi] as a | ||
990 | // child of the folder supplied as an argument. | ||
991 | // Note that a UUID may have been supplied as the | ||
992 | // identifier (it is the ONLY guaranteed unambiguous | ||
993 | // option. | ||
994 | |||
995 | if (rdata.folders != null) | ||
996 | { | ||
997 | foreach (InventoryFolderBase f in rdata.folders) | ||
998 | { | ||
999 | // Look for the present node in the directory list | ||
1000 | if (f.ParentID == folder.ID && | ||
1001 | (f.Name == rdata.Parameters[pi] || | ||
1002 | f.ID.ToString() == rdata.Parameters[pi])) | ||
1003 | { | ||
1004 | foundf = f; | ||
1005 | fk++; | ||
1006 | } | ||
1007 | } | ||
1008 | } | ||
1009 | |||
1010 | // If more than one node matched, then the path, as specified | ||
1011 | // is ambiguous. | ||
1012 | |||
1013 | if (fk > 1) | ||
1014 | { | ||
1015 | Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous", | ||
1016 | MsgId, rdata.method, rdata.path); | ||
1017 | rdata.Fail(Rest.HttpStatusCodeConflict, "request is ambiguous"); | ||
1018 | } | ||
1019 | |||
1020 | // If we find a match, then the method | ||
1021 | // increment the parameter index, and calls itself | ||
1022 | // passing the found folder as the new context. | ||
1023 | |||
1024 | if (foundf != null) | ||
1025 | { | ||
1026 | return getInventoryNode(rdata, foundf, pi+1, fill); | ||
1027 | } | ||
1028 | |||
1029 | // No folders that match. Perhaps this parameter identifies an item? If | ||
1030 | // it does, then it MUST also be the last name in the sequence. | ||
1031 | |||
1032 | if (pi == rdata.Parameters.Length-1) | ||
1033 | { | ||
1034 | if (rdata.items != null) | ||
1035 | { | ||
1036 | int k = 0; | ||
1037 | InventoryItemBase li = null; | ||
1038 | foreach (InventoryItemBase i in rdata.items) | ||
1039 | { | ||
1040 | if (i.Folder == folder.ID && | ||
1041 | (i.Name == rdata.Parameters[pi] || | ||
1042 | i.ID.ToString() == rdata.Parameters[pi])) | ||
1043 | { | ||
1044 | li = i; | ||
1045 | k++; | ||
1046 | } | ||
1047 | } | ||
1048 | if (k == 1) | ||
1049 | { | ||
1050 | return li; | ||
1051 | } | ||
1052 | else if (k > 1) | ||
1053 | { | ||
1054 | Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous", | ||
1055 | MsgId, rdata.method, rdata.path); | ||
1056 | rdata.Fail(Rest.HttpStatusCodeConflict, "request is ambiguous"); | ||
1057 | } | ||
1058 | } | ||
1059 | } | ||
1060 | |||
1061 | // If fill is enabled, then we must create the missing intermediate nodes. | ||
1062 | // And of course, even this is not straightforward. All intermediate nodes | ||
1063 | // are obviously folders, but the last node may be a folder or an item. | ||
1064 | |||
1065 | if (fill) | ||
1066 | { | ||
1067 | } | ||
1068 | |||
1069 | // No fill, so abandon the request | ||
1070 | |||
1071 | Rest.Log.DebugFormat("{0} {1}: Resource {2} not found", | ||
1072 | MsgId, rdata.method, rdata.path); | ||
1073 | rdata.Fail(Rest.HttpStatusCodeNotFound, | ||
1074 | String.Format("resource {0}:{1} not found", rdata.method, rdata.path)); | ||
1075 | |||
1076 | return null; /* Never reached */ | ||
1077 | } | ||
1078 | |||
1079 | /// <summary> | ||
1080 | /// This routine traverse the inventory's structure until the end-point identified | ||
1081 | /// in the URI is reached, the remainder of the inventory (if any) is then formatted | ||
1082 | /// and returned to the requestor. | ||
1083 | /// | ||
1084 | /// Note that this method is only interested in those folder that match elements of | ||
1085 | /// the URI supplied by the requestor, so once a match is fund, the processing does | ||
1086 | /// not need to consider any further elements. | ||
1087 | /// | ||
1088 | /// Only the last element in the URI should identify an item. | ||
1089 | /// </summary> | ||
1090 | /// <param name=rdata>HTTP service request work area</param> | ||
1091 | /// <param name=folder>The folder to be searched (parent)</param> | ||
1092 | /// <param name=pi>URI parameter index</param> | ||
1093 | |||
1094 | private void traverse(InventoryRequestData rdata, InventoryFolderBase folder, int pi) | ||
1095 | { | ||
1096 | Rest.Log.DebugFormat("{0} Traverse[initial] : {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi); | ||
1097 | |||
1098 | if (rdata.folders != null) | ||
1099 | { | ||
1100 | // If there was only one parameter (avatar name), then the entire | ||
1101 | // inventory is being requested. | ||
1102 | |||
1103 | if (rdata.Parameters.Length == 1) | ||
1104 | { | ||
1105 | formatInventory(rdata, rdata.root, String.Empty); | ||
1106 | } | ||
1107 | |||
1108 | // Has the client specified the root directory name explicitly? | ||
1109 | // if yes, then we just absorb the reference, because the folder | ||
1110 | // we start looking in for a match *is* the root directory. If there | ||
1111 | // are more parameters remaining we tarverse, otehrwise it's time | ||
1112 | // to format. Otherwise,we consider the "My Inventory" to be implied | ||
1113 | // and we just traverse normally. | ||
1114 | |||
1115 | else if (folder.ID.ToString() == rdata.Parameters[pi] || | ||
1116 | folder.Name == rdata.Parameters[pi]) | ||
1117 | { | ||
1118 | // Length is -1 because the avatar name is a parameter | ||
1119 | if (pi<(rdata.Parameters.Length-1)) | ||
1120 | { | ||
1121 | traverseInventory(rdata, folder, pi+1); | ||
1122 | } | ||
1123 | else | ||
1124 | { | ||
1125 | formatInventory(rdata, folder, String.Empty); | ||
1126 | } | ||
1127 | } | ||
1128 | else | ||
1129 | { | ||
1130 | traverseInventory(rdata, folder, pi); | ||
1131 | } | ||
1132 | |||
1133 | return; | ||
1134 | } | ||
1135 | } | ||
1136 | |||
1137 | /// <summary> | ||
1138 | /// This is the recursive method. I've separated them in this way so that | ||
1139 | /// we do not have to waste cycles on any first-case-only processing. | ||
1140 | /// </summary> | ||
1141 | |||
1142 | private void traverseInventory(InventoryRequestData rdata, InventoryFolderBase folder, int pi) | ||
1143 | { | ||
1144 | int fk = 0; | ||
1145 | InventoryFolderBase ffound = null; | ||
1146 | InventoryItemBase ifound = null; | ||
1147 | |||
1148 | Rest.Log.DebugFormat("{0} Traverse Folder : {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi); | ||
1149 | |||
1150 | foreach (InventoryFolderBase f in rdata.folders) | ||
1151 | { | ||
1152 | if (f.ParentID == folder.ID && | ||
1153 | (f.Name == rdata.Parameters[pi] || | ||
1154 | f.ID.ToString() == rdata.Parameters[pi])) | ||
1155 | { | ||
1156 | fk++; | ||
1157 | ffound = f; | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | // If this is the last element in the parameter sequence, then | ||
1162 | // it is reasonable to check for an item. All intermediate nodes | ||
1163 | // MUST be folders. | ||
1164 | |||
1165 | if (pi == rdata.Parameters.Length-1) | ||
1166 | { | ||
1167 | // Only if there are any items, and there pretty much always are. | ||
1168 | |||
1169 | if (rdata.items != null) | ||
1170 | { | ||
1171 | foreach (InventoryItemBase i in rdata.items) | ||
1172 | { | ||
1173 | if (i.Folder == folder.ID && | ||
1174 | (i.Name == rdata.Parameters[pi] || | ||
1175 | i.ID.ToString() == rdata.Parameters[pi])) | ||
1176 | { | ||
1177 | fk++; | ||
1178 | ifound = i; | ||
1179 | } | ||
1180 | } | ||
1181 | } | ||
1182 | } | ||
1183 | |||
1184 | if (fk == 1) | ||
1185 | { | ||
1186 | if (ffound != null) | ||
1187 | { | ||
1188 | if (pi < rdata.Parameters.Length-1) | ||
1189 | { | ||
1190 | traverseInventory(rdata, ffound, pi+1); | ||
1191 | } | ||
1192 | else | ||
1193 | { | ||
1194 | formatInventory(rdata, ffound, String.Empty); | ||
1195 | } | ||
1196 | return; | ||
1197 | } | ||
1198 | else | ||
1199 | { | ||
1200 | // Fetching an Item has a special significance. In this | ||
1201 | // case we also want to fetch the associated asset. | ||
1202 | // To make it interesting, we'll do this via redirection. | ||
1203 | string asseturl = String.Format("http://{0}:{1}/{2}{3}{4}", rdata.hostname, rdata.port, | ||
1204 | "admin/assets",Rest.UrlPathSeparator,ifound.AssetID.ToString()); | ||
1205 | rdata.Redirect(asseturl,Rest.PERMANENT); | ||
1206 | Rest.Log.DebugFormat("{0} Never Reached", MsgId); | ||
1207 | } | ||
1208 | } | ||
1209 | else if (fk > 1) | ||
1210 | { | ||
1211 | rdata.Fail(Rest.HttpStatusCodeConflict, | ||
1212 | String.Format("ambiguous element ({0}) in path specified: <{1}>", | ||
1213 | pi, rdata.path)); | ||
1214 | } | ||
1215 | |||
1216 | Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>", | ||
1217 | MsgId, rdata.path); | ||
1218 | rdata.Fail(Rest.HttpStatusCodeNotFound,String.Format("no such item/folder : {0}", | ||
1219 | rdata.Parameters[pi])); | ||
1220 | |||
1221 | } | ||
1222 | |||
1223 | /// <summary> | ||
1224 | /// This method generates XML that describes an instance of InventoryFolderBase. | ||
1225 | /// It recurses as necessary to reflect a folder hierarchy, and calls formatItem | ||
1226 | /// to generate XML for any items encountered along the way. | ||
1227 | /// The indentation parameter is solely for the benefit of trace record | ||
1228 | /// formatting. | ||
1229 | /// </summary> | ||
1230 | /// <param name=rdata>HTTP service request work area</param> | ||
1231 | /// <param name=folder>The folder to be searched (parent)</param> | ||
1232 | /// <param name=indent>pretty print indentation</param> | ||
1233 | private void formatInventory(InventoryRequestData rdata, InventoryFolderBase folder, string indent) | ||
1234 | { | ||
1235 | if (Rest.DEBUG) | ||
1236 | { | ||
1237 | Rest.Log.DebugFormat("{0} Folder : {1} {2} {3} type = {4}", | ||
1238 | MsgId, folder.ID, indent, folder.Name, folder.Type); | ||
1239 | indent += "\t"; | ||
1240 | } | ||
1241 | |||
1242 | // Start folder item | ||
1243 | |||
1244 | rdata.writer.WriteStartElement(String.Empty,"Folder",String.Empty); | ||
1245 | rdata.writer.WriteAttributeString("name",String.Empty,folder.Name); | ||
1246 | rdata.writer.WriteAttributeString("uuid",String.Empty,folder.ID.ToString()); | ||
1247 | rdata.writer.WriteAttributeString("parent",String.Empty,folder.ParentID.ToString()); | ||
1248 | rdata.writer.WriteAttributeString("owner",String.Empty,folder.Owner.ToString()); | ||
1249 | rdata.writer.WriteAttributeString("type",String.Empty,folder.Type.ToString()); | ||
1250 | rdata.writer.WriteAttributeString("version",String.Empty,folder.Version.ToString()); | ||
1251 | |||
1252 | if (rdata.folders != null) | ||
1253 | { | ||
1254 | foreach (InventoryFolderBase f in rdata.folders) | ||
1255 | { | ||
1256 | if (f.ParentID == folder.ID) | ||
1257 | { | ||
1258 | formatInventory(rdata, f, indent); | ||
1259 | } | ||
1260 | } | ||
1261 | } | ||
1262 | |||
1263 | if (rdata.items != null) | ||
1264 | { | ||
1265 | foreach (InventoryItemBase i in rdata.items) | ||
1266 | { | ||
1267 | if (i.Folder == folder.ID) | ||
1268 | { | ||
1269 | formatItem(rdata, i, indent); | ||
1270 | } | ||
1271 | } | ||
1272 | } | ||
1273 | |||
1274 | // End folder item | ||
1275 | |||
1276 | rdata.writer.WriteEndElement(); | ||
1277 | } | ||
1278 | |||
1279 | /// <summary> | ||
1280 | /// This method generates XML that describes an instance of InventoryItemBase. | ||
1281 | /// </summary> | ||
1282 | /// <param name="rdata">HTTP service request work area</param> | ||
1283 | /// <param name="i">The item to be formatted</param> | ||
1284 | /// <param name="indent">Pretty print indentation</param> | ||
1285 | private void formatItem(InventoryRequestData rdata, InventoryItemBase i, string indent) | ||
1286 | { | ||
1287 | Rest.Log.DebugFormat("{0} Item : {1} {2} {3} Type = {4}, AssetType = {5}", | ||
1288 | MsgId, i.ID, indent, i.Name, i.InvType, i.AssetType); | ||
1289 | |||
1290 | rdata.writer.WriteStartElement(String.Empty, "Item", String.Empty); | ||
1291 | |||
1292 | rdata.writer.WriteAttributeString("name", String.Empty, i.Name); | ||
1293 | rdata.writer.WriteAttributeString("desc", String.Empty, i.Description); | ||
1294 | rdata.writer.WriteAttributeString("uuid", String.Empty, i.ID.ToString()); | ||
1295 | rdata.writer.WriteAttributeString("folder", String.Empty, i.Folder.ToString()); | ||
1296 | rdata.writer.WriteAttributeString("owner", String.Empty, i.Owner.ToString()); | ||
1297 | rdata.writer.WriteAttributeString("creator", String.Empty, i.CreatorId); | ||
1298 | rdata.writer.WriteAttributeString("creatordata", String.Empty, i.CreatorData); | ||
1299 | rdata.writer.WriteAttributeString("creationdate", String.Empty, i.CreationDate.ToString()); | ||
1300 | rdata.writer.WriteAttributeString("invtype", String.Empty, i.InvType.ToString()); | ||
1301 | rdata.writer.WriteAttributeString("assettype", String.Empty, i.AssetType.ToString()); | ||
1302 | rdata.writer.WriteAttributeString("groupowned", String.Empty, i.GroupOwned.ToString()); | ||
1303 | rdata.writer.WriteAttributeString("groupid", String.Empty, i.GroupID.ToString()); | ||
1304 | rdata.writer.WriteAttributeString("saletype", String.Empty, i.SaleType.ToString()); | ||
1305 | rdata.writer.WriteAttributeString("saleprice", String.Empty, i.SalePrice.ToString()); | ||
1306 | rdata.writer.WriteAttributeString("flags", String.Empty, i.Flags.ToString()); | ||
1307 | |||
1308 | rdata.writer.WriteStartElement(String.Empty, "Permissions", String.Empty); | ||
1309 | rdata.writer.WriteAttributeString("current", String.Empty, i.CurrentPermissions.ToString("X")); | ||
1310 | rdata.writer.WriteAttributeString("next", String.Empty, i.NextPermissions.ToString("X")); | ||
1311 | rdata.writer.WriteAttributeString("group", String.Empty, i.GroupPermissions.ToString("X")); | ||
1312 | rdata.writer.WriteAttributeString("everyone", String.Empty, i.EveryOnePermissions.ToString("X")); | ||
1313 | rdata.writer.WriteAttributeString("base", String.Empty, i.BasePermissions.ToString("X")); | ||
1314 | rdata.writer.WriteEndElement(); | ||
1315 | |||
1316 | rdata.writer.WriteElementString("Asset", i.AssetID.ToString()); | ||
1317 | |||
1318 | rdata.writer.WriteEndElement(); | ||
1319 | } | ||
1320 | |||
1321 | /// <summary> | ||
1322 | /// This method creates a "trashcan" folder to support folder and item | ||
1323 | /// deletions by this interface. The xisting trash folder is found and | ||
1324 | /// this folder is created within it. It is called "tmp" to indicate to | ||
1325 | /// the client that it is OK to delete this folder. The REST interface | ||
1326 | /// will recreate the folder on an as-required basis. | ||
1327 | /// If the trash can cannot be created, then by implication the request | ||
1328 | /// that required it cannot be completed, and it fails accordingly. | ||
1329 | /// </summary> | ||
1330 | /// <param name=rdata>HTTP service request work area</param> | ||
1331 | private InventoryFolderBase GetTrashCan(InventoryRequestData rdata) | ||
1332 | { | ||
1333 | InventoryFolderBase TrashCan = null; | ||
1334 | |||
1335 | foreach (InventoryFolderBase f in rdata.folders) | ||
1336 | { | ||
1337 | if (f.Name == "Trash") | ||
1338 | { | ||
1339 | foreach (InventoryFolderBase t in rdata.folders) | ||
1340 | { | ||
1341 | if (t.Name == "tmp") | ||
1342 | { | ||
1343 | TrashCan = t; | ||
1344 | } | ||
1345 | } | ||
1346 | if (TrashCan == null) | ||
1347 | { | ||
1348 | TrashCan = new InventoryFolderBase(); | ||
1349 | TrashCan.Name = "tmp"; | ||
1350 | TrashCan.ID = UUID.Random(); | ||
1351 | TrashCan.Version = 1; | ||
1352 | TrashCan.Type = (short) AssetType.TrashFolder; | ||
1353 | TrashCan.ParentID = f.ID; | ||
1354 | TrashCan.Owner = f.Owner; | ||
1355 | Rest.InventoryServices.AddFolder(TrashCan); | ||
1356 | } | ||
1357 | } | ||
1358 | } | ||
1359 | |||
1360 | if (TrashCan == null) | ||
1361 | { | ||
1362 | Rest.Log.DebugFormat("{0} No Trash Can available", MsgId); | ||
1363 | rdata.Fail(Rest.HttpStatusCodeServerError, "unable to create trash can"); | ||
1364 | } | ||
1365 | |||
1366 | return TrashCan; | ||
1367 | } | ||
1368 | |||
1369 | /// <summary> | ||
1370 | /// Make sure that an unchanged folder is not unnecessarily | ||
1371 | /// processed. | ||
1372 | /// </summary> | ||
1373 | /// <param name=newf>Folder obtained from enclosed entity</param> | ||
1374 | /// <param name=oldf>Folder obtained from the user's inventory</param> | ||
1375 | private bool FolderHasChanged(InventoryFolderBase newf, InventoryFolderBase oldf) | ||
1376 | { | ||
1377 | return (newf.Name != oldf.Name | ||
1378 | || newf.ParentID != oldf.ParentID | ||
1379 | || newf.Owner != oldf.Owner | ||
1380 | || newf.Type != oldf.Type | ||
1381 | || newf.Version != oldf.Version | ||
1382 | ); | ||
1383 | } | ||
1384 | |||
1385 | /// <summary> | ||
1386 | /// Make sure that an unchanged item is not unnecessarily | ||
1387 | /// processed. | ||
1388 | /// </summary> | ||
1389 | /// <param name=newf>Item obtained from enclosed entity</param> | ||
1390 | /// <param name=oldf>Item obtained from the user's inventory</param> | ||
1391 | private bool ItemHasChanged(InventoryItemBase newf, InventoryItemBase oldf) | ||
1392 | { | ||
1393 | return (newf.Name != oldf.Name | ||
1394 | || newf.Folder != oldf.Folder | ||
1395 | || newf.Description != oldf.Description | ||
1396 | || newf.Owner != oldf.Owner | ||
1397 | || newf.CreatorId != oldf.CreatorId | ||
1398 | || newf.AssetID != oldf.AssetID | ||
1399 | || newf.GroupID != oldf.GroupID | ||
1400 | || newf.GroupOwned != oldf.GroupOwned | ||
1401 | || newf.InvType != oldf.InvType | ||
1402 | || newf.AssetType != oldf.AssetType | ||
1403 | ); | ||
1404 | } | ||
1405 | |||
1406 | /// <summary> | ||
1407 | /// This method is called by PUT and POST to create an XmlInventoryCollection | ||
1408 | /// instance that reflects the content of the entity supplied on the request. | ||
1409 | /// Any elements in the completed collection whose UUID is zero, are | ||
1410 | /// considered to be located relative to the end-point identified int he | ||
1411 | /// URI. In this way, an entire sub-tree can be conveyed in a single REST | ||
1412 | /// PUT or POST request. | ||
1413 | /// | ||
1414 | /// A new instance of XmlInventoryCollection is created and, if the request | ||
1415 | /// has an entity, it is more completely initialized. thus, if no entity was | ||
1416 | /// provided the collection is valid, but empty. | ||
1417 | /// | ||
1418 | /// The entity is then scanned and each tag is processed to produce the | ||
1419 | /// appropriate inventory elements. At the end f the scan, teh XmlInventoryCollection | ||
1420 | /// will reflect the subtree described by the entity. | ||
1421 | /// | ||
1422 | /// This is a very flexible mechanism, the entity may contain arbitrary, | ||
1423 | /// discontiguous tree fragments, or may contain single element. The caller is | ||
1424 | /// responsible for integrating this collection (and ensuring that any | ||
1425 | /// missing parent IDs are resolved). | ||
1426 | /// </summary> | ||
1427 | /// <param name=rdata>HTTP service request work area</param> | ||
1428 | internal XmlInventoryCollection ReconstituteEntity(InventoryRequestData rdata) | ||
1429 | { | ||
1430 | Rest.Log.DebugFormat("{0} Reconstituting entity", MsgId); | ||
1431 | |||
1432 | XmlInventoryCollection ic = new XmlInventoryCollection(); | ||
1433 | |||
1434 | if (rdata.request.HasEntityBody) | ||
1435 | { | ||
1436 | Rest.Log.DebugFormat("{0} Entity present", MsgId); | ||
1437 | |||
1438 | ic.init(rdata); | ||
1439 | |||
1440 | try | ||
1441 | { | ||
1442 | while (ic.xml.Read()) | ||
1443 | { | ||
1444 | switch (ic.xml.NodeType) | ||
1445 | { | ||
1446 | case XmlNodeType.Element: | ||
1447 | Rest.Log.DebugFormat("{0} StartElement: <{1}>", | ||
1448 | MsgId, ic.xml.Name); | ||
1449 | |||
1450 | switch (ic.xml.Name) | ||
1451 | { | ||
1452 | case "Folder": | ||
1453 | Rest.Log.DebugFormat("{0} Processing {1} element", | ||
1454 | MsgId, ic.xml.Name); | ||
1455 | CollectFolder(ic); | ||
1456 | break; | ||
1457 | case "Item": | ||
1458 | Rest.Log.DebugFormat("{0} Processing {1} element", | ||
1459 | MsgId, ic.xml.Name); | ||
1460 | CollectItem(ic); | ||
1461 | break; | ||
1462 | case "Asset": | ||
1463 | Rest.Log.DebugFormat("{0} Processing {1} element", | ||
1464 | MsgId, ic.xml.Name); | ||
1465 | CollectAsset(ic); | ||
1466 | break; | ||
1467 | case "Permissions": | ||
1468 | Rest.Log.DebugFormat("{0} Processing {1} element", | ||
1469 | MsgId, ic.xml.Name); | ||
1470 | CollectPermissions(ic); | ||
1471 | break; | ||
1472 | default: | ||
1473 | Rest.Log.DebugFormat("{0} Ignoring {1} element", | ||
1474 | MsgId, ic.xml.Name); | ||
1475 | break; | ||
1476 | } | ||
1477 | |||
1478 | // This stinks, but the ReadElement call above not only reads | ||
1479 | // the imbedded data, but also consumes the end tag for Asset | ||
1480 | // and moves the element pointer on to the containing Item's | ||
1481 | // element-end, however, if there was a permissions element | ||
1482 | // following, it would get us to the start of that.. | ||
1483 | if (ic.xml.NodeType == XmlNodeType.EndElement && | ||
1484 | ic.xml.Name == "Item") | ||
1485 | { | ||
1486 | Validate(ic); | ||
1487 | } | ||
1488 | break; | ||
1489 | |||
1490 | case XmlNodeType.EndElement : | ||
1491 | switch (ic.xml.Name) | ||
1492 | { | ||
1493 | case "Folder": | ||
1494 | Rest.Log.DebugFormat("{0} Completing {1} element", | ||
1495 | MsgId, ic.xml.Name); | ||
1496 | ic.Pop(); | ||
1497 | break; | ||
1498 | case "Item": | ||
1499 | Rest.Log.DebugFormat("{0} Completing {1} element", | ||
1500 | MsgId, ic.xml.Name); | ||
1501 | Validate(ic); | ||
1502 | break; | ||
1503 | case "Asset": | ||
1504 | Rest.Log.DebugFormat("{0} Completing {1} element", | ||
1505 | MsgId, ic.xml.Name); | ||
1506 | break; | ||
1507 | case "Permissions": | ||
1508 | Rest.Log.DebugFormat("{0} Completing {1} element", | ||
1509 | MsgId, ic.xml.Name); | ||
1510 | break; | ||
1511 | default: | ||
1512 | Rest.Log.DebugFormat("{0} Ignoring {1} element", | ||
1513 | MsgId, ic.xml.Name); | ||
1514 | break; | ||
1515 | } | ||
1516 | break; | ||
1517 | |||
1518 | default: | ||
1519 | Rest.Log.DebugFormat("{0} Ignoring: <{1}>:<{2}>", | ||
1520 | MsgId, ic.xml.NodeType, ic.xml.Value); | ||
1521 | break; | ||
1522 | } | ||
1523 | } | ||
1524 | } | ||
1525 | catch (XmlException e) | ||
1526 | { | ||
1527 | Rest.Log.WarnFormat("{0} XML parsing error: {1}", MsgId, e.Message); | ||
1528 | throw e; | ||
1529 | } | ||
1530 | catch (Exception e) | ||
1531 | { | ||
1532 | Rest.Log.WarnFormat("{0} Unexpected XML parsing error: {1}", MsgId, e.Message); | ||
1533 | throw e; | ||
1534 | } | ||
1535 | } | ||
1536 | else | ||
1537 | { | ||
1538 | Rest.Log.DebugFormat("{0} Entity absent", MsgId); | ||
1539 | } | ||
1540 | |||
1541 | if (Rest.DEBUG) | ||
1542 | { | ||
1543 | Rest.Log.DebugFormat("{0} Reconstituted entity", MsgId); | ||
1544 | Rest.Log.DebugFormat("{0} {1} assets", MsgId, ic.Assets.Count); | ||
1545 | Rest.Log.DebugFormat("{0} {1} folder", MsgId, ic.Folders.Count); | ||
1546 | Rest.Log.DebugFormat("{0} {1} items", MsgId, ic.Items.Count); | ||
1547 | } | ||
1548 | |||
1549 | return ic; | ||
1550 | } | ||
1551 | |||
1552 | /// <summary> | ||
1553 | /// This method creates an inventory Folder from the | ||
1554 | /// information supplied in the request's entity. | ||
1555 | /// A folder instance is created and initialized to reflect | ||
1556 | /// default values. These values are then overridden | ||
1557 | /// by information supplied in the entity. | ||
1558 | /// If context was not explicitly provided, then the | ||
1559 | /// appropriate ID values are determined. | ||
1560 | /// </summary> | ||
1561 | |||
1562 | private void CollectFolder(XmlInventoryCollection ic) | ||
1563 | { | ||
1564 | Rest.Log.DebugFormat("{0} Interpret folder element", MsgId); | ||
1565 | |||
1566 | InventoryFolderBase result = new InventoryFolderBase(); | ||
1567 | |||
1568 | // Default values | ||
1569 | |||
1570 | result.Name = String.Empty; | ||
1571 | result.ID = UUID.Zero; | ||
1572 | result.Owner = ic.UserID; | ||
1573 | result.ParentID = UUID.Zero; // Context | ||
1574 | result.Type = (short) AssetType.Folder; | ||
1575 | result.Version = 1; | ||
1576 | |||
1577 | if (ic.xml.HasAttributes) | ||
1578 | { | ||
1579 | for (int i = 0; i < ic.xml.AttributeCount; i++) | ||
1580 | { | ||
1581 | ic.xml.MoveToAttribute(i); | ||
1582 | switch (ic.xml.Name) | ||
1583 | { | ||
1584 | case "name": | ||
1585 | result.Name = ic.xml.Value; | ||
1586 | break; | ||
1587 | case "uuid": | ||
1588 | result.ID = new UUID(ic.xml.Value); | ||
1589 | break; | ||
1590 | case "parent": | ||
1591 | result.ParentID = new UUID(ic.xml.Value); | ||
1592 | break; | ||
1593 | case "owner": | ||
1594 | result.Owner = new UUID(ic.xml.Value); | ||
1595 | break; | ||
1596 | case "type": | ||
1597 | result.Type = Int16.Parse(ic.xml.Value); | ||
1598 | break; | ||
1599 | case "version": | ||
1600 | result.Version = UInt16.Parse(ic.xml.Value); | ||
1601 | break; | ||
1602 | default: | ||
1603 | Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}", | ||
1604 | MsgId, ic.xml.Name, ic.xml.Value); | ||
1605 | ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute <{0}>", | ||
1606 | ic.xml.Name)); | ||
1607 | break; | ||
1608 | } | ||
1609 | } | ||
1610 | } | ||
1611 | |||
1612 | ic.xml.MoveToElement(); | ||
1613 | |||
1614 | // The client is relying upon the reconstitution process | ||
1615 | // to determine the parent's UUID based upon context. This | ||
1616 | // is necessary where a new folder may have been | ||
1617 | // introduced. | ||
1618 | |||
1619 | if (result.ParentID == UUID.Zero) | ||
1620 | { | ||
1621 | result.ParentID = ic.Parent(); | ||
1622 | } | ||
1623 | else | ||
1624 | { | ||
1625 | bool found = false; | ||
1626 | |||
1627 | foreach (InventoryFolderBase parent in ic.rdata.folders) | ||
1628 | { | ||
1629 | if (parent.ID == result.ParentID) | ||
1630 | { | ||
1631 | found = true; | ||
1632 | break; | ||
1633 | } | ||
1634 | } | ||
1635 | |||
1636 | if (!found) | ||
1637 | { | ||
1638 | Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}", | ||
1639 | MsgId, ic.Item.Folder, result.ID); | ||
1640 | ic.Fail(Rest.HttpStatusCodeBadRequest, "invalid parent"); | ||
1641 | } | ||
1642 | } | ||
1643 | |||
1644 | // This is a new folder, so no existing UUID is available | ||
1645 | // or appropriate | ||
1646 | |||
1647 | if (result.ID == UUID.Zero) | ||
1648 | { | ||
1649 | result.ID = UUID.Random(); | ||
1650 | } | ||
1651 | |||
1652 | // Treat this as a new context. Any other information is | ||
1653 | // obsolete as a consequence. | ||
1654 | |||
1655 | ic.Push(result); | ||
1656 | } | ||
1657 | |||
1658 | /// <summary> | ||
1659 | /// This method is called to handle the construction of an Item | ||
1660 | /// instance from the supplied request entity. It is called | ||
1661 | /// whenever an Item start tag is detected. | ||
1662 | /// An instance of an Item is created and initialized to default | ||
1663 | /// values. These values are then overridden from values supplied | ||
1664 | /// as attributes to the Item element. | ||
1665 | /// This item is then stored in the XmlInventoryCollection and | ||
1666 | /// will be verified by Validate. | ||
1667 | /// All context is reset whenever the effective folder changes | ||
1668 | /// or an item is successfully validated. | ||
1669 | /// </summary> | ||
1670 | private void CollectItem(XmlInventoryCollection ic) | ||
1671 | { | ||
1672 | Rest.Log.DebugFormat("{0} Interpret item element", MsgId); | ||
1673 | |||
1674 | InventoryItemBase result = new InventoryItemBase(); | ||
1675 | |||
1676 | result.Name = String.Empty; | ||
1677 | result.Description = String.Empty; | ||
1678 | result.ID = UUID.Zero; | ||
1679 | result.Folder = UUID.Zero; | ||
1680 | result.Owner = ic.UserID; | ||
1681 | result.CreatorId = ic.UserID.ToString(); | ||
1682 | result.AssetID = UUID.Zero; | ||
1683 | result.GroupID = UUID.Zero; | ||
1684 | result.GroupOwned = false; | ||
1685 | result.InvType = (int) InventoryType.Unknown; | ||
1686 | result.AssetType = (int) AssetType.Unknown; | ||
1687 | |||
1688 | if (ic.xml.HasAttributes) | ||
1689 | { | ||
1690 | for (int i = 0; i < ic.xml.AttributeCount; i++) | ||
1691 | { | ||
1692 | ic.xml.MoveToAttribute(i); | ||
1693 | |||
1694 | switch (ic.xml.Name) | ||
1695 | { | ||
1696 | case "name": | ||
1697 | result.Name = ic.xml.Value; | ||
1698 | break; | ||
1699 | case "desc": | ||
1700 | result.Description = ic.xml.Value; | ||
1701 | break; | ||
1702 | case "uuid": | ||
1703 | result.ID = new UUID(ic.xml.Value); | ||
1704 | break; | ||
1705 | case "folder": | ||
1706 | result.Folder = new UUID(ic.xml.Value); | ||
1707 | break; | ||
1708 | case "owner": | ||
1709 | result.Owner = new UUID(ic.xml.Value); | ||
1710 | break; | ||
1711 | case "invtype": | ||
1712 | result.InvType = Int32.Parse(ic.xml.Value); | ||
1713 | break; | ||
1714 | case "creator": | ||
1715 | result.CreatorId = ic.xml.Value; | ||
1716 | break; | ||
1717 | case "assettype": | ||
1718 | result.AssetType = Int32.Parse(ic.xml.Value); | ||
1719 | break; | ||
1720 | case "groupowned": | ||
1721 | result.GroupOwned = Boolean.Parse(ic.xml.Value); | ||
1722 | break; | ||
1723 | case "groupid": | ||
1724 | result.GroupID = new UUID(ic.xml.Value); | ||
1725 | break; | ||
1726 | case "flags": | ||
1727 | result.Flags = UInt32.Parse(ic.xml.Value); | ||
1728 | break; | ||
1729 | case "creationdate": | ||
1730 | result.CreationDate = Int32.Parse(ic.xml.Value); | ||
1731 | break; | ||
1732 | case "saletype": | ||
1733 | result.SaleType = Byte.Parse(ic.xml.Value); | ||
1734 | break; | ||
1735 | case "saleprice": | ||
1736 | result.SalePrice = Int32.Parse(ic.xml.Value); | ||
1737 | break; | ||
1738 | |||
1739 | default: | ||
1740 | Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}", | ||
1741 | MsgId, ic.xml.Name, ic.xml.Value); | ||
1742 | ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute", | ||
1743 | ic.xml.Name)); | ||
1744 | break; | ||
1745 | } | ||
1746 | } | ||
1747 | } | ||
1748 | |||
1749 | ic.xml.MoveToElement(); | ||
1750 | |||
1751 | ic.Push(result); | ||
1752 | } | ||
1753 | |||
1754 | /// <summary> | ||
1755 | /// This method assembles an asset instance from the | ||
1756 | /// information supplied in the request's entity. It is | ||
1757 | /// called as a result of detecting a start tag for a | ||
1758 | /// type of Asset. | ||
1759 | /// The information is collected locally, and an asset | ||
1760 | /// instance is created only if the basic XML parsing | ||
1761 | /// completes successfully. | ||
1762 | /// Default values for all parts of the asset are | ||
1763 | /// established before overriding them from the supplied | ||
1764 | /// XML. | ||
1765 | /// If an asset has inline=true as an attribute, then | ||
1766 | /// the element contains the data representing the | ||
1767 | /// asset. This is saved as the data component. | ||
1768 | /// inline=false means that the element's payload is | ||
1769 | /// simply the UUID of the asset referenced by the | ||
1770 | /// item being constructed. | ||
1771 | /// An asset, if created is stored in the | ||
1772 | /// XmlInventoryCollection | ||
1773 | /// </summary> | ||
1774 | private void CollectAsset(XmlInventoryCollection ic) | ||
1775 | { | ||
1776 | Rest.Log.DebugFormat("{0} Interpret asset element", MsgId); | ||
1777 | |||
1778 | string name = String.Empty; | ||
1779 | string desc = String.Empty; | ||
1780 | sbyte type = (sbyte) AssetType.Unknown; | ||
1781 | bool temp = false; | ||
1782 | bool local = false; | ||
1783 | |||
1784 | // This is not a persistent attribute | ||
1785 | bool inline = false; | ||
1786 | |||
1787 | UUID uuid = UUID.Zero; | ||
1788 | |||
1789 | // Attribute is optional | ||
1790 | if (ic.xml.HasAttributes) | ||
1791 | { | ||
1792 | for (int i = 0; i < ic.xml.AttributeCount; i++) | ||
1793 | { | ||
1794 | ic.xml.MoveToAttribute(i); | ||
1795 | switch (ic.xml.Name) | ||
1796 | { | ||
1797 | case "name" : | ||
1798 | name = ic.xml.Value; | ||
1799 | break; | ||
1800 | |||
1801 | case "type" : | ||
1802 | type = SByte.Parse(ic.xml.Value); | ||
1803 | break; | ||
1804 | |||
1805 | case "description" : | ||
1806 | desc = ic.xml.Value; | ||
1807 | break; | ||
1808 | |||
1809 | case "temporary" : | ||
1810 | temp = Boolean.Parse(ic.xml.Value); | ||
1811 | break; | ||
1812 | |||
1813 | case "uuid" : | ||
1814 | uuid = new UUID(ic.xml.Value); | ||
1815 | break; | ||
1816 | |||
1817 | case "inline" : | ||
1818 | inline = Boolean.Parse(ic.xml.Value); | ||
1819 | break; | ||
1820 | |||
1821 | case "local" : | ||
1822 | local = Boolean.Parse(ic.xml.Value); | ||
1823 | break; | ||
1824 | |||
1825 | default : | ||
1826 | Rest.Log.DebugFormat("{0} Asset: Unrecognized attribute: {1}:{2}", | ||
1827 | MsgId, ic.xml.Name, ic.xml.Value); | ||
1828 | ic.Fail(Rest.HttpStatusCodeBadRequest, | ||
1829 | String.Format("unrecognized attribute <{0}>", ic.xml.Name)); | ||
1830 | break; | ||
1831 | } | ||
1832 | } | ||
1833 | } | ||
1834 | |||
1835 | ic.xml.MoveToElement(); | ||
1836 | |||
1837 | // If this is a reference to an existing asset, just store the | ||
1838 | // asset ID into the item. | ||
1839 | |||
1840 | if (!inline) | ||
1841 | { | ||
1842 | if (ic.Item != null) | ||
1843 | { | ||
1844 | ic.Item.AssetID = new UUID(ic.xml.ReadElementContentAsString()); | ||
1845 | Rest.Log.DebugFormat("{0} Asset ID supplied: {1}", MsgId, ic.Item.AssetID); | ||
1846 | } | ||
1847 | else | ||
1848 | { | ||
1849 | Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId); | ||
1850 | ic.Fail(Rest.HttpStatusCodeBadRequest, "no context for asset"); | ||
1851 | } | ||
1852 | } | ||
1853 | |||
1854 | // Otherwise, generate an asset ID, store that into the item, and | ||
1855 | // create an entry in the asset list for the inlined asset. But | ||
1856 | // only if the size is non-zero. | ||
1857 | |||
1858 | else | ||
1859 | { | ||
1860 | AssetBase asset = null; | ||
1861 | string b64string = null; | ||
1862 | |||
1863 | // Generate a UUID if none were given, and generally none should | ||
1864 | // be. Ever. | ||
1865 | |||
1866 | if (uuid == UUID.Zero) | ||
1867 | { | ||
1868 | uuid = UUID.Random(); | ||
1869 | } | ||
1870 | |||
1871 | // Create AssetBase entity to hold the inlined asset | ||
1872 | |||
1873 | asset = new AssetBase(uuid, name, type, UUID.Zero.ToString()); | ||
1874 | |||
1875 | asset.Description = desc; | ||
1876 | asset.Local = local; | ||
1877 | asset.Temporary = temp; | ||
1878 | |||
1879 | b64string = ic.xml.ReadElementContentAsString(); | ||
1880 | |||
1881 | Rest.Log.DebugFormat("{0} Data length is {1}", MsgId, b64string.Length); | ||
1882 | Rest.Log.DebugFormat("{0} Data content starts with: \n\t<{1}>", MsgId, | ||
1883 | b64string.Substring(0, b64string.Length > 132 ? 132 : b64string.Length)); | ||
1884 | |||
1885 | asset.Data = Convert.FromBase64String(b64string); | ||
1886 | |||
1887 | // Ensure the asset always has some kind of data component | ||
1888 | |||
1889 | if (asset.Data == null) | ||
1890 | { | ||
1891 | asset.Data = new byte[1]; | ||
1892 | } | ||
1893 | |||
1894 | // If this is in the context of an item, establish | ||
1895 | // a link with the item in context. | ||
1896 | |||
1897 | if (ic.Item != null && ic.Item.AssetID == UUID.Zero) | ||
1898 | { | ||
1899 | ic.Item.AssetID = uuid; | ||
1900 | } | ||
1901 | |||
1902 | ic.Push(asset); | ||
1903 | } | ||
1904 | } | ||
1905 | |||
1906 | /// <summary> | ||
1907 | /// Store any permissions information provided by the request. | ||
1908 | /// This overrides the default permissions set when the | ||
1909 | /// XmlInventoryCollection object was created. | ||
1910 | /// </summary> | ||
1911 | private void CollectPermissions(XmlInventoryCollection ic) | ||
1912 | { | ||
1913 | if (ic.xml.HasAttributes) | ||
1914 | { | ||
1915 | for (int i = 0; i < ic.xml.AttributeCount; i++) | ||
1916 | { | ||
1917 | ic.xml.MoveToAttribute(i); | ||
1918 | switch (ic.xml.Name) | ||
1919 | { | ||
1920 | case "current": | ||
1921 | ic.CurrentPermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber); | ||
1922 | break; | ||
1923 | case "next": | ||
1924 | ic.NextPermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber); | ||
1925 | break; | ||
1926 | case "group": | ||
1927 | ic.GroupPermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber); | ||
1928 | break; | ||
1929 | case "everyone": | ||
1930 | ic.EveryOnePermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber); | ||
1931 | break; | ||
1932 | case "base": | ||
1933 | ic.BasePermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber); | ||
1934 | break; | ||
1935 | default: | ||
1936 | Rest.Log.DebugFormat("{0} Permissions: invalid attribute {1}:{2}", | ||
1937 | MsgId,ic.xml.Name, ic.xml.Value); | ||
1938 | ic.Fail(Rest.HttpStatusCodeBadRequest, | ||
1939 | String.Format("invalid attribute <{0}>", ic.xml.Name)); | ||
1940 | break; | ||
1941 | } | ||
1942 | } | ||
1943 | } | ||
1944 | |||
1945 | ic.xml.MoveToElement(); | ||
1946 | } | ||
1947 | |||
1948 | /// <summary> | ||
1949 | /// This method is called whenever an Item has been successfully | ||
1950 | /// reconstituted from the request's entity. | ||
1951 | /// It uses the information curren tin the XmlInventoryCollection | ||
1952 | /// to complete the item's specification, including any implied | ||
1953 | /// context and asset associations. | ||
1954 | /// It fails the request if any necessary item or asset information | ||
1955 | /// is missing. | ||
1956 | /// </summary> | ||
1957 | |||
1958 | private void Validate(XmlInventoryCollection ic) | ||
1959 | { | ||
1960 | // There really should be an item present if we've | ||
1961 | // called validate. So fail if there is not. | ||
1962 | |||
1963 | if (ic.Item == null) | ||
1964 | { | ||
1965 | Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId); | ||
1966 | ic.Fail(Rest.HttpStatusCodeBadRequest, "request parse error"); | ||
1967 | } | ||
1968 | |||
1969 | // Every item is required to have a name (via REST anyway) | ||
1970 | |||
1971 | if (ic.Item.Name == String.Empty) | ||
1972 | { | ||
1973 | Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId); | ||
1974 | ic.Fail(Rest.HttpStatusCodeBadRequest, "item name required"); | ||
1975 | } | ||
1976 | |||
1977 | // An item MUST have an asset ID. AssetID should never be zero | ||
1978 | // here. It should always get set from the information stored | ||
1979 | // when the Asset element was processed. | ||
1980 | |||
1981 | if (ic.Item.AssetID == UUID.Zero) | ||
1982 | { | ||
1983 | Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId); | ||
1984 | Rest.Log.InfoFormat("{0} Asset information is missing", MsgId); | ||
1985 | ic.Fail(Rest.HttpStatusCodeBadRequest, "asset information required"); | ||
1986 | } | ||
1987 | |||
1988 | // If the item is new, then assign it an ID | ||
1989 | |||
1990 | if (ic.Item.ID == UUID.Zero) | ||
1991 | { | ||
1992 | ic.Item.ID = UUID.Random(); | ||
1993 | } | ||
1994 | |||
1995 | // If the context is being implied, obtain the current | ||
1996 | // folder item's ID. If it was specified explicitly, make | ||
1997 | // sure that theparent folder exists. | ||
1998 | |||
1999 | if (ic.Item.Folder == UUID.Zero) | ||
2000 | { | ||
2001 | ic.Item.Folder = ic.Parent(); | ||
2002 | } | ||
2003 | else | ||
2004 | { | ||
2005 | bool found = false; | ||
2006 | |||
2007 | foreach (InventoryFolderBase parent in ic.rdata.folders) | ||
2008 | { | ||
2009 | if (parent.ID == ic.Item.Folder) | ||
2010 | { | ||
2011 | found = true; | ||
2012 | break; | ||
2013 | } | ||
2014 | } | ||
2015 | |||
2016 | if (!found) | ||
2017 | { | ||
2018 | Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}", | ||
2019 | MsgId, ic.Item.Folder, ic.Item.ID); | ||
2020 | ic.Fail(Rest.HttpStatusCodeBadRequest, "parent information required"); | ||
2021 | } | ||
2022 | } | ||
2023 | |||
2024 | // If this is an inline asset being constructed in the context | ||
2025 | // of a new Item, then use the itm's name here too. | ||
2026 | |||
2027 | if (ic.Asset != null) | ||
2028 | { | ||
2029 | if (ic.Asset.Name == String.Empty) | ||
2030 | ic.Asset.Name = ic.Item.Name; | ||
2031 | if (ic.Asset.Description == String.Empty) | ||
2032 | ic.Asset.Description = ic.Item.Description; | ||
2033 | } | ||
2034 | |||
2035 | // Assign permissions | ||
2036 | |||
2037 | ic.Item.CurrentPermissions = ic.CurrentPermissions; | ||
2038 | ic.Item.EveryOnePermissions = ic.EveryOnePermissions; | ||
2039 | ic.Item.BasePermissions = ic.BasePermissions; | ||
2040 | ic.Item.GroupPermissions = ic.GroupPermissions; | ||
2041 | ic.Item.NextPermissions = ic.NextPermissions; | ||
2042 | |||
2043 | // If no type was specified for this item, we can attempt to | ||
2044 | // infer something from the file type maybe. This is NOT as | ||
2045 | // good as having type be specified in the XML. | ||
2046 | |||
2047 | if (ic.Item.AssetType == (int) AssetType.Unknown || | ||
2048 | ic.Item.InvType == (int) InventoryType.Unknown) | ||
2049 | { | ||
2050 | Rest.Log.DebugFormat("{0} Attempting to infer item type", MsgId); | ||
2051 | |||
2052 | string[] parts = ic.Item.Name.Split(Rest.CA_PERIOD); | ||
2053 | |||
2054 | if (Rest.DEBUG) | ||
2055 | { | ||
2056 | for (int i = 0; i < parts.Length; i++) | ||
2057 | { | ||
2058 | Rest.Log.DebugFormat("{0} Name part {1} : {2}", | ||
2059 | MsgId, i, parts[i]); | ||
2060 | } | ||
2061 | } | ||
2062 | |||
2063 | // If the associated item name is multi-part, then maybe | ||
2064 | // the last part will indicate the item type - if we're | ||
2065 | // lucky. | ||
2066 | |||
2067 | if (parts.Length > 1) | ||
2068 | { | ||
2069 | Rest.Log.DebugFormat("{0} File type is {1}", | ||
2070 | MsgId, parts[parts.Length - 1]); | ||
2071 | switch (parts[parts.Length - 1]) | ||
2072 | { | ||
2073 | case "jpeg2000" : | ||
2074 | case "jpeg-2000" : | ||
2075 | case "jpg2000" : | ||
2076 | case "jpg-2000" : | ||
2077 | Rest.Log.DebugFormat("{0} Type {1} inferred", | ||
2078 | MsgId, parts[parts.Length-1]); | ||
2079 | if (ic.Item.AssetType == (int) AssetType.Unknown) | ||
2080 | ic.Item.AssetType = (int) AssetType.ImageJPEG; | ||
2081 | if (ic.Item.InvType == (int) InventoryType.Unknown) | ||
2082 | ic.Item.InvType = (int) InventoryType.Texture; | ||
2083 | break; | ||
2084 | case "jpg" : | ||
2085 | case "jpeg" : | ||
2086 | Rest.Log.DebugFormat("{0} Type {1} inferred", | ||
2087 | MsgId, parts[parts.Length - 1]); | ||
2088 | if (ic.Item.AssetType == (int) AssetType.Unknown) | ||
2089 | ic.Item.AssetType = (int) AssetType.ImageJPEG; | ||
2090 | if (ic.Item.InvType == (int) InventoryType.Unknown) | ||
2091 | ic.Item.InvType = (int) InventoryType.Texture; | ||
2092 | break; | ||
2093 | case "tga" : | ||
2094 | if (parts[parts.Length - 2].IndexOf("_texture") != -1) | ||
2095 | { | ||
2096 | if (ic.Item.AssetType == (int) AssetType.Unknown) | ||
2097 | ic.Item.AssetType = (int) AssetType.TextureTGA; | ||
2098 | if (ic.Item.InvType == (int) AssetType.Unknown) | ||
2099 | ic.Item.InvType = (int) InventoryType.Texture; | ||
2100 | } | ||
2101 | else | ||
2102 | { | ||
2103 | if (ic.Item.AssetType == (int) AssetType.Unknown) | ||
2104 | ic.Item.AssetType = (int) AssetType.ImageTGA; | ||
2105 | if (ic.Item.InvType == (int) InventoryType.Unknown) | ||
2106 | ic.Item.InvType = (int) InventoryType.Snapshot; | ||
2107 | } | ||
2108 | break; | ||
2109 | default : | ||
2110 | Rest.Log.DebugFormat("{0} Asset/Inventory type could not be inferred for {1}", | ||
2111 | MsgId,ic.Item.Name); | ||
2112 | break; | ||
2113 | } | ||
2114 | } | ||
2115 | } | ||
2116 | |||
2117 | /// If this is a TGA remember the fact | ||
2118 | |||
2119 | if (ic.Item.AssetType == (int) AssetType.TextureTGA || | ||
2120 | ic.Item.AssetType == (int) AssetType.ImageTGA) | ||
2121 | { | ||
2122 | Bitmap temp; | ||
2123 | Stream tgadata = new MemoryStream(ic.Asset.Data); | ||
2124 | |||
2125 | temp = LoadTGAClass.LoadTGA(tgadata); | ||
2126 | try | ||
2127 | { | ||
2128 | ic.Asset.Data = OpenJPEG.EncodeFromImage(temp, true); | ||
2129 | } | ||
2130 | catch (DllNotFoundException) | ||
2131 | { | ||
2132 | Rest.Log.ErrorFormat("OpenJpeg is not installed correctly on this system. Asset Data is empty for {0}", ic.Item.Name); | ||
2133 | ic.Asset.Data = new Byte[0]; | ||
2134 | } | ||
2135 | catch (IndexOutOfRangeException) | ||
2136 | { | ||
2137 | Rest.Log.ErrorFormat("OpenJpeg was unable to encode this. Asset Data is empty for {0}", ic.Item.Name); | ||
2138 | ic.Asset.Data = new Byte[0]; | ||
2139 | } | ||
2140 | catch (Exception) | ||
2141 | { | ||
2142 | Rest.Log.ErrorFormat("OpenJpeg was unable to encode this. Asset Data is empty for {0}", ic.Item.Name); | ||
2143 | ic.Asset.Data = new Byte[0]; | ||
2144 | } | ||
2145 | } | ||
2146 | |||
2147 | ic.reset(); | ||
2148 | } | ||
2149 | |||
2150 | #region Inventory RequestData extension | ||
2151 | |||
2152 | internal class InventoryRequestData : RequestData | ||
2153 | { | ||
2154 | /// <summary> | ||
2155 | /// These are the inventory specific request/response state | ||
2156 | /// extensions. | ||
2157 | /// </summary> | ||
2158 | |||
2159 | internal UUID uuid = UUID.Zero; | ||
2160 | internal bool HaveInventory = false; | ||
2161 | internal ICollection<InventoryFolderImpl> folders = null; | ||
2162 | internal ICollection<InventoryItemBase> items = null; | ||
2163 | internal UserProfileData userProfile = null; | ||
2164 | internal InventoryFolderBase root = null; | ||
2165 | internal bool timeout = false; | ||
2166 | internal Timer watchDog = new Timer(); | ||
2167 | |||
2168 | internal InventoryRequestData(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
2169 | : base(request, response, prefix) | ||
2170 | { | ||
2171 | } | ||
2172 | |||
2173 | internal void startWD(double interval) | ||
2174 | { | ||
2175 | Rest.Log.DebugFormat("{0} Setting watchdog", MsgId); | ||
2176 | watchDog.Elapsed += new ElapsedEventHandler(OnTimeOut); | ||
2177 | watchDog.Interval = interval; | ||
2178 | watchDog.AutoReset = false; | ||
2179 | watchDog.Enabled = true; | ||
2180 | lock (watchDog) | ||
2181 | watchDog.Start(); | ||
2182 | |||
2183 | } | ||
2184 | |||
2185 | internal void stopWD() | ||
2186 | { | ||
2187 | Rest.Log.DebugFormat("{0} Reset watchdog", MsgId); | ||
2188 | lock (watchDog) | ||
2189 | watchDog.Stop(); | ||
2190 | } | ||
2191 | |||
2192 | /// <summary> | ||
2193 | /// This is the callback method required by the inventory watchdog. The | ||
2194 | /// requestor issues an inventory request and then blocks until the | ||
2195 | /// request completes, or this method signals the monitor. | ||
2196 | /// </summary> | ||
2197 | |||
2198 | private void OnTimeOut(object sender, ElapsedEventArgs args) | ||
2199 | { | ||
2200 | Rest.Log.DebugFormat("{0} Asynchronous inventory update timed-out", MsgId); | ||
2201 | // InventoryRequestData rdata = (InventoryRequestData) sender; | ||
2202 | lock (this) | ||
2203 | { | ||
2204 | this.folders = null; | ||
2205 | this.items = null; | ||
2206 | this.HaveInventory = false; | ||
2207 | this.timeout = true; | ||
2208 | Monitor.Pulse(this); | ||
2209 | } | ||
2210 | } | ||
2211 | |||
2212 | /// <summary> | ||
2213 | /// This is the callback method required by inventory services. The | ||
2214 | /// requestor issues an inventory request and then blocks until this | ||
2215 | /// method signals the monitor. | ||
2216 | /// </summary> | ||
2217 | |||
2218 | internal void GetUserInventory(ICollection<InventoryFolderImpl> folders, ICollection<InventoryItemBase> items) | ||
2219 | { | ||
2220 | Rest.Log.DebugFormat("{0} Asynchronously updating inventory data", MsgId); | ||
2221 | lock (this) | ||
2222 | { | ||
2223 | if (watchDog.Enabled) | ||
2224 | { | ||
2225 | this.stopWD(); | ||
2226 | } | ||
2227 | this.folders = folders; | ||
2228 | this.items = items; | ||
2229 | this.HaveInventory = true; | ||
2230 | this.timeout = false; | ||
2231 | Monitor.Pulse(this); | ||
2232 | } | ||
2233 | } | ||
2234 | } | ||
2235 | |||
2236 | #endregion Inventory RequestData extension | ||
2237 | |||
2238 | /// <summary> | ||
2239 | /// This class is used to record and manage the hierarchy | ||
2240 | /// constructed from the entity supplied in the request for | ||
2241 | /// PUT and POST. | ||
2242 | /// </summary> | ||
2243 | |||
2244 | internal class XmlInventoryCollection : InventoryCollection | ||
2245 | { | ||
2246 | internal InventoryRequestData rdata; | ||
2247 | private Stack<InventoryFolderBase> stk; | ||
2248 | |||
2249 | internal List<AssetBase> Assets; | ||
2250 | |||
2251 | internal InventoryItemBase Item; | ||
2252 | internal AssetBase Asset; | ||
2253 | internal XmlReader xml; | ||
2254 | |||
2255 | internal /*static*/ const uint DefaultCurrent = 0x7FFFFFFF; | ||
2256 | internal /*static*/ const uint DefaultNext = 0x82000; | ||
2257 | internal /*static*/ const uint DefaultBase = 0x7FFFFFFF; | ||
2258 | internal /*static*/ const uint DefaultEveryOne = 0x0; | ||
2259 | internal /*static*/ const uint DefaultGroup = 0x0; | ||
2260 | |||
2261 | internal uint CurrentPermissions = 0x00; | ||
2262 | internal uint NextPermissions = 0x00; | ||
2263 | internal uint BasePermissions = 0x00; | ||
2264 | internal uint EveryOnePermissions = 0x00; | ||
2265 | internal uint GroupPermissions = 0x00; | ||
2266 | |||
2267 | internal XmlInventoryCollection() | ||
2268 | { | ||
2269 | Folders = new List<InventoryFolderBase>(); | ||
2270 | Items = new List<InventoryItemBase>(); | ||
2271 | Assets = new List<AssetBase>(); | ||
2272 | } | ||
2273 | |||
2274 | internal void init(InventoryRequestData p_rdata) | ||
2275 | { | ||
2276 | rdata = p_rdata; | ||
2277 | UserID = rdata.uuid; | ||
2278 | stk = new Stack<InventoryFolderBase>(); | ||
2279 | rdata.initXmlReader(); | ||
2280 | xml = rdata.reader; | ||
2281 | initPermissions(); | ||
2282 | } | ||
2283 | |||
2284 | internal void initPermissions() | ||
2285 | { | ||
2286 | CurrentPermissions = DefaultCurrent; | ||
2287 | NextPermissions = DefaultNext; | ||
2288 | BasePermissions = DefaultBase; | ||
2289 | GroupPermissions = DefaultGroup; | ||
2290 | EveryOnePermissions = DefaultEveryOne; | ||
2291 | } | ||
2292 | |||
2293 | internal UUID Parent() | ||
2294 | { | ||
2295 | if (stk.Count != 0) | ||
2296 | { | ||
2297 | return stk.Peek().ID; | ||
2298 | } | ||
2299 | else | ||
2300 | { | ||
2301 | return UUID.Zero; | ||
2302 | } | ||
2303 | } | ||
2304 | |||
2305 | internal void Push(InventoryFolderBase folder) | ||
2306 | { | ||
2307 | stk.Push(folder); | ||
2308 | Folders.Add(folder); | ||
2309 | reset(); | ||
2310 | } | ||
2311 | |||
2312 | internal void Push(InventoryItemBase item) | ||
2313 | { | ||
2314 | Item = item; | ||
2315 | Items.Add(item); | ||
2316 | } | ||
2317 | |||
2318 | internal void Push(AssetBase asset) | ||
2319 | { | ||
2320 | Asset = asset; | ||
2321 | Assets.Add(asset); | ||
2322 | } | ||
2323 | |||
2324 | internal void Pop() | ||
2325 | { | ||
2326 | stk.Pop(); | ||
2327 | reset(); | ||
2328 | } | ||
2329 | |||
2330 | internal void reset() | ||
2331 | { | ||
2332 | Item = null; | ||
2333 | Asset = null; | ||
2334 | initPermissions(); | ||
2335 | } | ||
2336 | |||
2337 | internal void Fail(int code, string addendum) | ||
2338 | { | ||
2339 | rdata.Fail(code, addendum); | ||
2340 | } | ||
2341 | } | ||
2342 | } | ||
2343 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestTestServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestTestServices.cs deleted file mode 100644 index 81596a3..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestTestServices.cs +++ /dev/null | |||
@@ -1,246 +0,0 @@ | |||
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 | |||
29 | using System; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using OpenSim.Framework.Servers; | ||
33 | using OpenSim.Framework.Servers.HttpServer; | ||
34 | |||
35 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
36 | { | ||
37 | public class RestTestServices : IRest | ||
38 | { | ||
39 | private bool enabled = false; | ||
40 | private string qPrefix = "test"; | ||
41 | |||
42 | // A simple constructor is used to handle any once-only | ||
43 | // initialization of working classes. | ||
44 | |||
45 | public RestTestServices() | ||
46 | { | ||
47 | Rest.Log.InfoFormat("{0} Test services initializing", MsgId); | ||
48 | Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); | ||
49 | |||
50 | // If a relative path was specified, make it absolute by adding | ||
51 | // the standard prefix, e.g. /admin | ||
52 | |||
53 | if (!qPrefix.StartsWith(Rest.UrlPathSeparator)) | ||
54 | { | ||
55 | Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId); | ||
56 | qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix); | ||
57 | Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix); | ||
58 | } | ||
59 | |||
60 | // Load test cases | ||
61 | |||
62 | loadTests(); | ||
63 | foreach (ITest test in tests) | ||
64 | { | ||
65 | test.Initialize(); | ||
66 | } | ||
67 | |||
68 | // Register interface | ||
69 | |||
70 | Rest.Plugin.AddPathHandler(DoTests,qPrefix,Allocate); | ||
71 | |||
72 | // Activate | ||
73 | |||
74 | enabled = true; | ||
75 | |||
76 | Rest.Log.InfoFormat("{0} Test services initialization complete", MsgId); | ||
77 | } | ||
78 | |||
79 | // Post-construction, pre-enabled initialization opportunity | ||
80 | // Not currently exploited. | ||
81 | |||
82 | public void Initialize() | ||
83 | { | ||
84 | } | ||
85 | |||
86 | // Called by the plug-in to halt REST processing. Local processing is | ||
87 | // disabled, and control blocks until all current processing has | ||
88 | // completed. No new processing will be started | ||
89 | |||
90 | public void Close() | ||
91 | { | ||
92 | enabled = false; | ||
93 | foreach (ITest test in tests) | ||
94 | { | ||
95 | test.Close(); | ||
96 | } | ||
97 | Rest.Log.InfoFormat("{0} Test services closing down", MsgId); | ||
98 | } | ||
99 | |||
100 | // Properties | ||
101 | |||
102 | internal string MsgId | ||
103 | { | ||
104 | get { return Rest.MsgId; } | ||
105 | } | ||
106 | |||
107 | #region Interface | ||
108 | |||
109 | private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix) | ||
110 | { | ||
111 | return new RequestData(request, response, prefix); | ||
112 | } | ||
113 | |||
114 | // Inventory Handler | ||
115 | |||
116 | private void DoTests(RequestData rdata) | ||
117 | { | ||
118 | if (!enabled) | ||
119 | return; | ||
120 | |||
121 | // Now that we know this is a serious attempt to | ||
122 | // access inventory data, we should find out who | ||
123 | // is asking, and make sure they are authorized | ||
124 | // to do so. We need to validate the caller's | ||
125 | // identity before revealing anything about the | ||
126 | // status quo. Authenticate throws an exception | ||
127 | // via Fail if no identity information is present. | ||
128 | // | ||
129 | // With the present HTTP server we can't use the | ||
130 | // builtin authentication mechanisms because they | ||
131 | // would be enforced for all in-bound requests. | ||
132 | // Instead we look at the headers ourselves and | ||
133 | // handle authentication directly. | ||
134 | |||
135 | try | ||
136 | { | ||
137 | if (!rdata.IsAuthenticated) | ||
138 | { | ||
139 | rdata.Fail(Rest.HttpStatusCodeNotAuthorized, | ||
140 | String.Format("user \"{0}\" could not be authenticated", rdata.userName)); | ||
141 | } | ||
142 | } | ||
143 | catch (RestException e) | ||
144 | { | ||
145 | if (e.statusCode == Rest.HttpStatusCodeNotAuthorized) | ||
146 | { | ||
147 | Rest.Log.WarnFormat("{0} User not authenticated", MsgId); | ||
148 | Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization")); | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | Rest.Log.ErrorFormat("{0} User authentication failed", MsgId); | ||
153 | Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization")); | ||
154 | } | ||
155 | throw (e); | ||
156 | } | ||
157 | |||
158 | // Check that a test was specified | ||
159 | |||
160 | if (rdata.Parameters.Length < 1) | ||
161 | { | ||
162 | Rest.Log.DebugFormat("{0} Insufficient parameters", MsgId); | ||
163 | rdata.Fail(Rest.HttpStatusCodeBadRequest, "not enough parameters"); | ||
164 | } | ||
165 | |||
166 | // Select the test | ||
167 | |||
168 | foreach (ITest test in tests) | ||
169 | { | ||
170 | if (!rdata.handled) | ||
171 | test.Execute(rdata); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | #endregion Interface | ||
176 | |||
177 | private static bool testsLoaded = false; | ||
178 | private static List<Type> classes = new List<Type>(); | ||
179 | private static List<ITest> tests = new List<ITest>(); | ||
180 | private static Type[] parms = new Type[0]; | ||
181 | private static Object[] args = new Object[0]; | ||
182 | |||
183 | static RestTestServices() | ||
184 | { | ||
185 | Module[] mods = Assembly.GetExecutingAssembly().GetModules(); | ||
186 | foreach (Module m in mods) | ||
187 | { | ||
188 | Type[] types = m.GetTypes(); | ||
189 | foreach (Type t in types) | ||
190 | { | ||
191 | try | ||
192 | { | ||
193 | if (t.GetInterface("ITest") != null) | ||
194 | { | ||
195 | classes.Add(t); | ||
196 | } | ||
197 | } | ||
198 | catch (Exception e) | ||
199 | { | ||
200 | Rest.Log.WarnFormat("[STATIC-TEST] Unable to include test {0} : {1}", t, e.Message); | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /// <summary> | ||
207 | /// This routine loads all of the handlers discovered during | ||
208 | /// instance initialization. Each handler is responsible for | ||
209 | /// registering itself with this handler. | ||
210 | /// I was not able to make this code work in a constructor. | ||
211 | /// </summary> | ||
212 | |||
213 | private void loadTests() | ||
214 | { | ||
215 | lock (tests) | ||
216 | { | ||
217 | if (!testsLoaded) | ||
218 | { | ||
219 | |||
220 | ConstructorInfo ci; | ||
221 | Object ht; | ||
222 | |||
223 | foreach (Type t in classes) | ||
224 | { | ||
225 | try | ||
226 | { | ||
227 | if (t.GetInterface("ITest") != null) | ||
228 | { | ||
229 | ci = t.GetConstructor(parms); | ||
230 | ht = ci.Invoke(args); | ||
231 | tests.Add((ITest)ht); | ||
232 | Rest.Log.InfoFormat("{0} Test {1} added", MsgId, t); | ||
233 | } | ||
234 | } | ||
235 | catch (Exception e) | ||
236 | { | ||
237 | Rest.Log.WarnFormat("{0} Unable to load test {1} : {2}", MsgId, t, e.Message); | ||
238 | } | ||
239 | } | ||
240 | testsLoaded = true; | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | |||
245 | } | ||
246 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/ITest.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/tests/ITest.cs deleted file mode 100644 index eafc154..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/ITest.cs +++ /dev/null | |||
@@ -1,46 +0,0 @@ | |||
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 | |||
29 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
30 | { | ||
31 | |||
32 | /// <summary> | ||
33 | /// This interface represents the boundary between the general purpose | ||
34 | /// REST plugin handling, and the functionally specific handlers. The | ||
35 | /// handler knows only to initialzie and terminate all such handlers | ||
36 | /// that it finds. | ||
37 | /// </summary> | ||
38 | |||
39 | internal interface ITest | ||
40 | { | ||
41 | void Initialize(); | ||
42 | void Execute(RequestData rdata); | ||
43 | void Close(); | ||
44 | } | ||
45 | |||
46 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/Remote.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/tests/Remote.cs deleted file mode 100644 index 1c1afd0..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/Remote.cs +++ /dev/null | |||
@@ -1,204 +0,0 @@ | |||
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 OpenMetaverse; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.ApplicationPlugins.Rest.Inventory.Tests | ||
33 | { | ||
34 | public class Remote : ITest | ||
35 | { | ||
36 | private static readonly int PARM_TESTID = 0; | ||
37 | private static readonly int PARM_COMMAND = 1; | ||
38 | |||
39 | private static readonly int PARM_MOVE_AVATAR = 2; | ||
40 | private static readonly int PARM_MOVE_X = 3; | ||
41 | private static readonly int PARM_MOVE_Y = 4; | ||
42 | private static readonly int PARM_MOVE_Z = 5; | ||
43 | |||
44 | private bool enabled = false; | ||
45 | |||
46 | // No constructor code is required. | ||
47 | |||
48 | public Remote() | ||
49 | { | ||
50 | Rest.Log.InfoFormat("{0} Remote services constructor", MsgId); | ||
51 | } | ||
52 | |||
53 | // Post-construction, pre-enabled initialization opportunity | ||
54 | // Not currently exploited. | ||
55 | |||
56 | public void Initialize() | ||
57 | { | ||
58 | enabled = true; | ||
59 | Rest.Log.InfoFormat("{0} Remote services initialized", MsgId); | ||
60 | } | ||
61 | |||
62 | // Called by the plug-in to halt REST processing. Local processing is | ||
63 | // disabled, and control blocks until all current processing has | ||
64 | // completed. No new processing will be started | ||
65 | |||
66 | public void Close() | ||
67 | { | ||
68 | enabled = false; | ||
69 | Rest.Log.InfoFormat("{0} Remote services closing down", MsgId); | ||
70 | } | ||
71 | |||
72 | // Properties | ||
73 | |||
74 | internal string MsgId | ||
75 | { | ||
76 | get { return Rest.MsgId; } | ||
77 | } | ||
78 | |||
79 | // Remote Handler | ||
80 | // Key information of interest here is the Parameters array, each | ||
81 | // entry represents an element of the URI, with element zero being | ||
82 | // the | ||
83 | |||
84 | public void Execute(RequestData rdata) | ||
85 | { | ||
86 | if (!enabled) return; | ||
87 | |||
88 | // If we can't relate to what's there, leave it for others. | ||
89 | |||
90 | if (rdata.Parameters.Length == 0 || rdata.Parameters[PARM_TESTID] != "remote") | ||
91 | return; | ||
92 | |||
93 | Rest.Log.DebugFormat("{0} REST Remote handler ENTRY", MsgId); | ||
94 | |||
95 | // Remove the prefix and what's left are the parameters. If we don't have | ||
96 | // the parameters we need, fail the request. Parameters do NOT include | ||
97 | // any supplied query values. | ||
98 | |||
99 | if (rdata.Parameters.Length > 1) | ||
100 | { | ||
101 | switch (rdata.Parameters[PARM_COMMAND].ToLower()) | ||
102 | { | ||
103 | case "move" : | ||
104 | DoMove(rdata); | ||
105 | break; | ||
106 | default : | ||
107 | DoHelp(rdata); | ||
108 | break; | ||
109 | } | ||
110 | } | ||
111 | else | ||
112 | { | ||
113 | DoHelp(rdata); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | private void DoHelp(RequestData rdata) | ||
118 | { | ||
119 | rdata.body = Help; | ||
120 | rdata.Complete(); | ||
121 | rdata.Respond("Help"); | ||
122 | } | ||
123 | |||
124 | private void DoMove(RequestData rdata) | ||
125 | { | ||
126 | if (rdata.Parameters.Length < 6) | ||
127 | { | ||
128 | Rest.Log.WarnFormat("{0} Move: No movement information provided", MsgId); | ||
129 | rdata.Fail(Rest.HttpStatusCodeBadRequest, "no movement information provided"); | ||
130 | } | ||
131 | else | ||
132 | { | ||
133 | string[] names = rdata.Parameters[PARM_MOVE_AVATAR].Split(Rest.CA_SPACE); | ||
134 | ScenePresence presence = null; | ||
135 | Scene scene = null; | ||
136 | |||
137 | if (names.Length != 2) | ||
138 | { | ||
139 | rdata.Fail(Rest.HttpStatusCodeBadRequest, | ||
140 | String.Format("invalid avatar name: <{0}>",rdata.Parameters[PARM_MOVE_AVATAR])); | ||
141 | } | ||
142 | |||
143 | Rest.Log.WarnFormat("{0} '{1}' command received for {2} {3}", | ||
144 | MsgId, rdata.Parameters[0], names[0], names[1]); | ||
145 | |||
146 | // The first parameter should be an avatar name, look for the | ||
147 | // avatar in the known regions first. | ||
148 | |||
149 | Rest.main.SceneManager.ForEachScene(delegate(Scene s) | ||
150 | { | ||
151 | s.ForEachRootScenePresence(delegate(ScenePresence sp) | ||
152 | { | ||
153 | if (sp.Firstname == names[0] && sp.Lastname == names[1]) | ||
154 | { | ||
155 | scene = s; | ||
156 | presence = sp; | ||
157 | } | ||
158 | }); | ||
159 | }); | ||
160 | |||
161 | if (presence != null) | ||
162 | { | ||
163 | Rest.Log.DebugFormat("{0} Move : Avatar {1} located in region {2}", | ||
164 | MsgId, rdata.Parameters[PARM_MOVE_AVATAR], scene.RegionInfo.RegionName); | ||
165 | |||
166 | try | ||
167 | { | ||
168 | float x = Convert.ToSingle(rdata.Parameters[PARM_MOVE_X]); | ||
169 | float y = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Y]); | ||
170 | float z = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Z]); | ||
171 | Vector3 vector = new Vector3(x, y, z); | ||
172 | presence.MoveToTarget(vector, false, false); | ||
173 | } | ||
174 | catch (Exception e) | ||
175 | { | ||
176 | rdata.Fail(Rest.HttpStatusCodeBadRequest, | ||
177 | String.Format("invalid parameters: {0}", e.Message)); | ||
178 | } | ||
179 | } | ||
180 | else | ||
181 | { | ||
182 | rdata.Fail(Rest.HttpStatusCodeBadRequest, | ||
183 | String.Format("avatar {0} not present", rdata.Parameters[PARM_MOVE_AVATAR])); | ||
184 | } | ||
185 | |||
186 | rdata.Complete(); | ||
187 | rdata.Respond("OK"); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | private static readonly string Help = | ||
192 | "<html>" | ||
193 | + "<head><title>Remote Command Usage</title></head>" | ||
194 | + "<body>" | ||
195 | + "<p>Supported commands are:</p>" | ||
196 | + "<dl>" | ||
197 | + "<dt>move/avatar-name/x/y/z</dt>" | ||
198 | + "<dd>moves the specified avatar to another location</dd>" | ||
199 | + "</dl>" | ||
200 | + "</body>" | ||
201 | + "</html>" | ||
202 | ; | ||
203 | } | ||
204 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Regions/GETHandler.cs b/OpenSim/ApplicationPlugins/Rest/Regions/GETHandler.cs deleted file mode 100644 index d99ba57..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Regions/GETHandler.cs +++ /dev/null | |||
@@ -1,228 +0,0 @@ | |||
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.Xml.Serialization; | ||
31 | using OpenMetaverse; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Framework.Servers; | ||
34 | using OpenSim.Framework.Servers.HttpServer; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.Framework.Scenes; | ||
37 | |||
38 | namespace OpenSim.ApplicationPlugins.Rest.Regions | ||
39 | { | ||
40 | public partial class RestRegionPlugin : RestPlugin | ||
41 | { | ||
42 | #region GET methods | ||
43 | public string GetHandler(string request, string path, string param, | ||
44 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | ||
45 | { | ||
46 | // foreach (string h in httpRequest.Headers.AllKeys) | ||
47 | // foreach (string v in httpRequest.Headers.GetValues(h)) | ||
48 | // m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v); | ||
49 | |||
50 | MsgID = RequestID; | ||
51 | m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param); | ||
52 | |||
53 | try | ||
54 | { | ||
55 | // param empty: regions list | ||
56 | if (String.IsNullOrEmpty(param)) return GetHandlerRegions(httpResponse); | ||
57 | |||
58 | // param not empty: specific region | ||
59 | return GetHandlerRegion(httpResponse, param); | ||
60 | } | ||
61 | catch (Exception e) | ||
62 | { | ||
63 | return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | public string GetHandlerRegions(IOSHttpResponse httpResponse) | ||
68 | { | ||
69 | RestXmlWriter rxw = new RestXmlWriter(new StringWriter()); | ||
70 | |||
71 | rxw.WriteStartElement(String.Empty, "regions", String.Empty); | ||
72 | foreach (Scene s in App.SceneManager.Scenes) | ||
73 | { | ||
74 | rxw.WriteStartElement(String.Empty, "uuid", String.Empty); | ||
75 | rxw.WriteString(s.RegionInfo.RegionID.ToString()); | ||
76 | rxw.WriteEndElement(); | ||
77 | } | ||
78 | rxw.WriteEndElement(); | ||
79 | |||
80 | return rxw.ToString(); | ||
81 | } | ||
82 | |||
83 | protected string ShortRegionInfo(string key, string value) | ||
84 | { | ||
85 | RestXmlWriter rxw = new RestXmlWriter(new StringWriter()); | ||
86 | |||
87 | if (String.IsNullOrEmpty(value) || | ||
88 | String.IsNullOrEmpty(key)) return null; | ||
89 | |||
90 | rxw.WriteStartElement(String.Empty, "region", String.Empty); | ||
91 | rxw.WriteStartElement(String.Empty, key, String.Empty); | ||
92 | rxw.WriteString(value); | ||
93 | rxw.WriteEndDocument(); | ||
94 | |||
95 | return rxw.ToString(); | ||
96 | } | ||
97 | |||
98 | public string GetHandlerRegion(IOSHttpResponse httpResponse, string param) | ||
99 | { | ||
100 | // be resilient and don't get confused by a terminating '/' | ||
101 | param = param.TrimEnd(new char[]{'/'}); | ||
102 | string[] comps = param.Split('/'); | ||
103 | UUID regionID = (UUID)comps[0]; | ||
104 | |||
105 | m_log.DebugFormat("{0} GET region UUID {1}", MsgID, regionID.ToString()); | ||
106 | |||
107 | if (UUID.Zero == regionID) throw new Exception("missing region ID"); | ||
108 | |||
109 | Scene scene = null; | ||
110 | App.SceneManager.TryGetScene(regionID, out scene); | ||
111 | if (null == scene) return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound, | ||
112 | "GET", "cannot find region {0}", regionID.ToString()); | ||
113 | |||
114 | RegionDetails details = new RegionDetails(scene.RegionInfo); | ||
115 | |||
116 | // m_log.DebugFormat("{0} GET comps {1}", MsgID, comps.Length); | ||
117 | // for (int i = 0; i < comps.Length; i++) m_log.DebugFormat("{0} GET comps[{1}] >{2}<", MsgID, i, comps[i]); | ||
118 | |||
119 | if (1 == comps.Length) | ||
120 | { | ||
121 | // complete region details requested | ||
122 | RestXmlWriter rxw = new RestXmlWriter(new StringWriter()); | ||
123 | XmlSerializer xs = new XmlSerializer(typeof(RegionDetails)); | ||
124 | xs.Serialize(rxw, details, _xmlNs); | ||
125 | return rxw.ToString(); | ||
126 | } | ||
127 | |||
128 | if (2 == comps.Length) | ||
129 | { | ||
130 | string resp = ShortRegionInfo(comps[1], details[comps[1]]); | ||
131 | if (null != resp) return resp; | ||
132 | |||
133 | // m_log.DebugFormat("{0} GET comps advanced: >{1}<", MsgID, comps[1]); | ||
134 | |||
135 | // check for {terrain,stats,prims} | ||
136 | switch (comps[1].ToLower()) | ||
137 | { | ||
138 | case "terrain": | ||
139 | return RegionTerrain(httpResponse, scene); | ||
140 | |||
141 | case "stats": | ||
142 | return RegionStats(httpResponse, scene); | ||
143 | |||
144 | case "prims": | ||
145 | return RegionPrims(httpResponse, scene, Vector3.Zero, Vector3.Zero); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | if (3 == comps.Length) | ||
150 | { | ||
151 | switch (comps[1].ToLower()) | ||
152 | { | ||
153 | case "prims": | ||
154 | string[] subregion = comps[2].Split(','); | ||
155 | if (subregion.Length == 6) | ||
156 | { | ||
157 | Vector3 min, max; | ||
158 | try | ||
159 | { | ||
160 | min = new Vector3((float)Double.Parse(subregion[0], Culture.NumberFormatInfo), (float)Double.Parse(subregion[1], Culture.NumberFormatInfo), (float)Double.Parse(subregion[2], Culture.NumberFormatInfo)); | ||
161 | max = new Vector3((float)Double.Parse(subregion[3], Culture.NumberFormatInfo), (float)Double.Parse(subregion[4], Culture.NumberFormatInfo), (float)Double.Parse(subregion[5], Culture.NumberFormatInfo)); | ||
162 | } | ||
163 | catch (Exception) | ||
164 | { | ||
165 | return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest, | ||
166 | "GET", "invalid subregion parameter"); | ||
167 | } | ||
168 | return RegionPrims(httpResponse, scene, min, max); | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest, | ||
173 | "GET", "invalid subregion parameter"); | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest, | ||
179 | "GET", "too many parameters {0}", param); | ||
180 | } | ||
181 | #endregion GET methods | ||
182 | |||
183 | protected string RegionTerrain(IOSHttpResponse httpResponse, Scene scene) | ||
184 | { | ||
185 | httpResponse.SendChunked = true; | ||
186 | httpResponse.ContentType = "text/xml"; | ||
187 | |||
188 | return scene.Heightmap.SaveToXmlString(); | ||
189 | //return Failure(httpResponse, OSHttpStatusCode.ServerErrorNotImplemented, | ||
190 | // "GET", "terrain not implemented"); | ||
191 | } | ||
192 | |||
193 | protected string RegionStats(IOSHttpResponse httpResponse, Scene scene) | ||
194 | { | ||
195 | int users = scene.GetRootAgentCount(); | ||
196 | int objects = scene.Entities.Count - users; | ||
197 | |||
198 | RestXmlWriter rxw = new RestXmlWriter(new StringWriter()); | ||
199 | |||
200 | rxw.WriteStartElement(String.Empty, "region", String.Empty); | ||
201 | rxw.WriteStartElement(String.Empty, "stats", String.Empty); | ||
202 | |||
203 | rxw.WriteStartElement(String.Empty, "users", String.Empty); | ||
204 | rxw.WriteString(users.ToString()); | ||
205 | rxw.WriteEndElement(); | ||
206 | |||
207 | rxw.WriteStartElement(String.Empty, "objects", String.Empty); | ||
208 | rxw.WriteString(objects.ToString()); | ||
209 | rxw.WriteEndElement(); | ||
210 | |||
211 | rxw.WriteEndDocument(); | ||
212 | |||
213 | return rxw.ToString(); | ||
214 | } | ||
215 | |||
216 | protected string RegionPrims(IOSHttpResponse httpResponse, Scene scene, Vector3 min, Vector3 max) | ||
217 | { | ||
218 | httpResponse.SendChunked = true; | ||
219 | httpResponse.ContentType = "text/xml"; | ||
220 | |||
221 | IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>(); | ||
222 | if (serialiser != null) | ||
223 | serialiser.SavePrimsToXml2(scene, new StreamWriter(httpResponse.OutputStream), min, max); | ||
224 | |||
225 | return ""; | ||
226 | } | ||
227 | } | ||
228 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Regions/GETRegionInfoHandler.cs b/OpenSim/ApplicationPlugins/Rest/Regions/GETRegionInfoHandler.cs deleted file mode 100644 index 468faea..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Regions/GETRegionInfoHandler.cs +++ /dev/null | |||
@@ -1,136 +0,0 @@ | |||
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.Xml.Serialization; | ||
31 | using OpenMetaverse; | ||
32 | using OpenSim.Framework.Servers; | ||
33 | using OpenSim.Framework.Servers.HttpServer; | ||
34 | using OpenSim.Region.Framework.Interfaces; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | |||
37 | namespace OpenSim.ApplicationPlugins.Rest.Regions | ||
38 | { | ||
39 | public partial class RestRegionPlugin : RestPlugin | ||
40 | { | ||
41 | #region GET methods | ||
42 | public string GetRegionInfoHandler(string request, string path, string param, | ||
43 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | ||
44 | { | ||
45 | // foreach (string h in httpRequest.Headers.AllKeys) | ||
46 | // foreach (string v in httpRequest.Headers.GetValues(h)) | ||
47 | // m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v); | ||
48 | |||
49 | MsgID = RequestID; | ||
50 | m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param); | ||
51 | |||
52 | try | ||
53 | { | ||
54 | // param empty: regions list | ||
55 | // if (String.IsNullOrEmpty(param)) | ||
56 | return GetRegionInfoHandlerRegions(httpResponse); | ||
57 | |||
58 | // // param not empty: specific region | ||
59 | // return GetRegionInfoHandlerRegion(httpResponse, param); | ||
60 | } | ||
61 | catch (Exception e) | ||
62 | { | ||
63 | return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | public string GetRegionInfoHandlerRegions(IOSHttpResponse httpResponse) | ||
68 | { | ||
69 | RestXmlWriter rxw = new RestXmlWriter(new StringWriter()); | ||
70 | |||
71 | // regions info | ||
72 | rxw.WriteStartElement(String.Empty, "regions", String.Empty); | ||
73 | { | ||
74 | // regions info: number of regions | ||
75 | rxw.WriteStartAttribute(String.Empty, "number", String.Empty); | ||
76 | rxw.WriteValue(App.SceneManager.Scenes.Count); | ||
77 | rxw.WriteEndAttribute(); | ||
78 | |||
79 | // regions info: max number of regions | ||
80 | rxw.WriteStartAttribute(String.Empty, "max", String.Empty); | ||
81 | if (App.ConfigSource.Source.Configs["RemoteAdmin"] != null) | ||
82 | { | ||
83 | rxw.WriteValue(App.ConfigSource.Source.Configs["RemoteAdmin"].GetInt("region_limit", -1)); | ||
84 | } | ||
85 | else | ||
86 | { | ||
87 | rxw.WriteValue(-1); | ||
88 | } | ||
89 | rxw.WriteEndAttribute(); | ||
90 | |||
91 | // regions info: region | ||
92 | foreach (Scene s in App.SceneManager.Scenes) | ||
93 | { | ||
94 | rxw.WriteStartElement(String.Empty, "region", String.Empty); | ||
95 | |||
96 | rxw.WriteStartAttribute(String.Empty, "uuid", String.Empty); | ||
97 | rxw.WriteString(s.RegionInfo.RegionID.ToString()); | ||
98 | rxw.WriteEndAttribute(); | ||
99 | |||
100 | rxw.WriteStartAttribute(String.Empty, "name", String.Empty); | ||
101 | rxw.WriteString(s.RegionInfo.RegionName); | ||
102 | rxw.WriteEndAttribute(); | ||
103 | |||
104 | rxw.WriteStartAttribute(String.Empty, "x", String.Empty); | ||
105 | rxw.WriteValue(s.RegionInfo.RegionLocX); | ||
106 | rxw.WriteEndAttribute(); | ||
107 | |||
108 | rxw.WriteStartAttribute(String.Empty, "y", String.Empty); | ||
109 | rxw.WriteValue(s.RegionInfo.RegionLocY); | ||
110 | rxw.WriteEndAttribute(); | ||
111 | |||
112 | rxw.WriteStartAttribute(String.Empty, "external_hostname", String.Empty); | ||
113 | rxw.WriteString(s.RegionInfo.ExternalHostName); | ||
114 | rxw.WriteEndAttribute(); | ||
115 | |||
116 | rxw.WriteStartAttribute(String.Empty, "ip", String.Empty); | ||
117 | rxw.WriteString(s.RegionInfo.InternalEndPoint.ToString()); | ||
118 | rxw.WriteEndAttribute(); | ||
119 | |||
120 | int users = s.GetRootAgentCount(); | ||
121 | rxw.WriteStartAttribute(String.Empty, "avatars", String.Empty); | ||
122 | rxw.WriteValue(users); | ||
123 | rxw.WriteEndAttribute(); | ||
124 | |||
125 | rxw.WriteStartAttribute(String.Empty, "objects", String.Empty); | ||
126 | rxw.WriteValue(s.Entities.Count - users); | ||
127 | rxw.WriteEndAttribute(); | ||
128 | |||
129 | rxw.WriteEndElement(); | ||
130 | } | ||
131 | } | ||
132 | return rxw.ToString(); | ||
133 | } | ||
134 | #endregion GET methods | ||
135 | } | ||
136 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Regions/POSTHandler.cs b/OpenSim/ApplicationPlugins/Rest/Regions/POSTHandler.cs deleted file mode 100644 index f666f45..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Regions/POSTHandler.cs +++ /dev/null | |||
@@ -1,122 +0,0 @@ | |||
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 OpenMetaverse; | ||
31 | using OpenSim.Framework.Servers; | ||
32 | using OpenSim.Framework.Servers.HttpServer; | ||
33 | using OpenSim.Region.Framework.Interfaces; | ||
34 | using OpenSim.Region.Framework.Scenes; | ||
35 | |||
36 | namespace OpenSim.ApplicationPlugins.Rest.Regions | ||
37 | { | ||
38 | public partial class RestRegionPlugin : RestPlugin | ||
39 | { | ||
40 | #region POST methods | ||
41 | |||
42 | public string PostHandler(string request, string path, string param, | ||
43 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | ||
44 | { | ||
45 | // foreach (string h in httpRequest.Headers.AllKeys) | ||
46 | // foreach (string v in httpRequest.Headers.GetValues(h)) | ||
47 | // m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v); | ||
48 | |||
49 | MsgID = RequestID; | ||
50 | m_log.DebugFormat("{0} POST path {1} param {2}", MsgID, path, param); | ||
51 | |||
52 | try | ||
53 | { | ||
54 | // param empty: new region post | ||
55 | if (!IsGod(httpRequest)) | ||
56 | // XXX: this needs to be turned into a FailureUnauthorized(...) | ||
57 | return Failure(httpResponse, OSHttpStatusCode.ClientErrorUnauthorized, | ||
58 | "GET", "you are not god"); | ||
59 | |||
60 | if (String.IsNullOrEmpty(param)) return CreateRegion(httpRequest, httpResponse); | ||
61 | |||
62 | // Parse region ID and other parameters | ||
63 | param = param.TrimEnd(new char[] {'/'}); | ||
64 | string[] comps = param.Split('/'); | ||
65 | UUID regionID = (UUID) comps[0]; | ||
66 | |||
67 | m_log.DebugFormat("{0} POST region UUID {1}", MsgID, regionID.ToString()); | ||
68 | if (UUID.Zero == regionID) throw new Exception("missing region ID"); | ||
69 | |||
70 | Scene scene = null; | ||
71 | App.SceneManager.TryGetScene(regionID, out scene); | ||
72 | if (null == scene) | ||
73 | return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound, | ||
74 | "POST", "cannot find region {0}", regionID.ToString()); | ||
75 | |||
76 | if (2 == comps.Length) | ||
77 | { | ||
78 | // check for {prims} | ||
79 | switch (comps[1].ToLower()) | ||
80 | { | ||
81 | case "prims": | ||
82 | return LoadPrims(request, httpRequest, httpResponse, scene); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound, | ||
87 | "POST", "url {0} not supported", param); | ||
88 | } | ||
89 | catch (Exception e) | ||
90 | { | ||
91 | return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "POST", e); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | public string CreateRegion(IOSHttpRequest request, IOSHttpResponse response) | ||
96 | { | ||
97 | RestXmlWriter rxw = new RestXmlWriter(new StringWriter()); | ||
98 | |||
99 | rxw.WriteStartElement(String.Empty, "regions", String.Empty); | ||
100 | foreach (Scene s in App.SceneManager.Scenes) | ||
101 | { | ||
102 | rxw.WriteStartElement(String.Empty, "uuid", String.Empty); | ||
103 | rxw.WriteString(s.RegionInfo.RegionID.ToString()); | ||
104 | rxw.WriteEndElement(); | ||
105 | } | ||
106 | rxw.WriteEndElement(); | ||
107 | |||
108 | return rxw.ToString(); | ||
109 | } | ||
110 | |||
111 | public string LoadPrims(string requestBody, IOSHttpRequest request, IOSHttpResponse response, Scene scene) | ||
112 | { | ||
113 | IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>(); | ||
114 | if (serialiser != null) | ||
115 | serialiser.LoadPrimsFromXml2(scene, new StringReader(requestBody), true); | ||
116 | |||
117 | return ""; | ||
118 | } | ||
119 | |||
120 | #endregion POST methods | ||
121 | } | ||
122 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Regions/RegionDetails.cs b/OpenSim/ApplicationPlugins/Rest/Regions/RegionDetails.cs deleted file mode 100644 index 5e76009..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Regions/RegionDetails.cs +++ /dev/null | |||
@@ -1,98 +0,0 @@ | |||
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.Xml.Serialization; | ||
30 | using OpenMetaverse; | ||
31 | using OpenSim.Framework; | ||
32 | |||
33 | namespace OpenSim.ApplicationPlugins.Rest.Regions | ||
34 | { | ||
35 | [XmlRoot(ElementName="region", IsNullable = false)] | ||
36 | public class RegionDetails | ||
37 | { | ||
38 | public string region_name; | ||
39 | public string region_id; | ||
40 | public uint region_x; | ||
41 | public uint region_y; | ||
42 | public string region_owner; | ||
43 | public string region_owner_id; | ||
44 | public uint region_http_port; | ||
45 | public uint region_port; | ||
46 | public string region_server_uri; | ||
47 | public string region_external_hostname; | ||
48 | |||
49 | public RegionDetails() | ||
50 | { | ||
51 | } | ||
52 | |||
53 | public RegionDetails(RegionInfo regInfo) | ||
54 | { | ||
55 | region_name = regInfo.RegionName; | ||
56 | region_id = regInfo.RegionID.ToString(); | ||
57 | region_x = regInfo.RegionLocX; | ||
58 | region_y = regInfo.RegionLocY; | ||
59 | region_owner_id = regInfo.EstateSettings.EstateOwner.ToString(); | ||
60 | region_http_port = regInfo.HttpPort; | ||
61 | region_server_uri = regInfo.ServerURI; | ||
62 | region_external_hostname = regInfo.ExternalHostName; | ||
63 | |||
64 | Uri uri = new Uri(region_server_uri); | ||
65 | region_port = (uint)uri.Port; | ||
66 | } | ||
67 | |||
68 | public string this[string idx] | ||
69 | { | ||
70 | get | ||
71 | { | ||
72 | switch (idx.ToLower()) | ||
73 | { | ||
74 | case "name": | ||
75 | return region_name; | ||
76 | case "id": | ||
77 | return region_id; | ||
78 | case "location": | ||
79 | return String.Format("<x>{0}</x><y>{1}</y>", region_x, region_y); | ||
80 | case "owner": | ||
81 | return region_owner; | ||
82 | case "owner_id": | ||
83 | return region_owner_id; | ||
84 | case "http_port": | ||
85 | return region_http_port.ToString(); | ||
86 | case "server_uri": | ||
87 | return region_server_uri; | ||
88 | case "external_hostname": | ||
89 | case "hostname": | ||
90 | return region_external_hostname; | ||
91 | |||
92 | default: | ||
93 | return null; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Regions/Resources/RestRegionPlugin.addin.xml b/OpenSim/ApplicationPlugins/Rest/Regions/Resources/RestRegionPlugin.addin.xml deleted file mode 100644 index 94eca48..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Regions/Resources/RestRegionPlugin.addin.xml +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | <Addin id="OpenSim.ApplicationPlugins.Rest.Regions" version="0.1"> | ||
2 | <Runtime> | ||
3 | <Import assembly="OpenSim.ApplicationPlugins.Rest.Regions.dll"/> | ||
4 | </Runtime> | ||
5 | <Dependencies> | ||
6 | <Addin id="OpenSim" version="0.5" /> | ||
7 | </Dependencies> | ||
8 | <Extension path = "/OpenSim/Startup"> | ||
9 | <Plugin id="RestRegions" type="OpenSim.ApplicationPlugins.Rest.Regions.RestRegionPlugin" /> | ||
10 | </Extension> | ||
11 | </Addin> | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/Regions/RestRegionPlugin.cs b/OpenSim/ApplicationPlugins/Rest/Regions/RestRegionPlugin.cs deleted file mode 100644 index 02ef588..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Regions/RestRegionPlugin.cs +++ /dev/null | |||
@@ -1,94 +0,0 @@ | |||
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.Xml.Serialization; | ||
30 | |||
31 | namespace OpenSim.ApplicationPlugins.Rest.Regions | ||
32 | { | ||
33 | public partial class RestRegionPlugin : RestPlugin | ||
34 | { | ||
35 | private static XmlSerializerNamespaces _xmlNs; | ||
36 | |||
37 | static RestRegionPlugin() | ||
38 | { | ||
39 | _xmlNs = new XmlSerializerNamespaces(); | ||
40 | _xmlNs.Add(String.Empty, String.Empty); | ||
41 | } | ||
42 | |||
43 | #region overriding properties | ||
44 | public override string Name | ||
45 | { | ||
46 | get { return "REGION"; } | ||
47 | } | ||
48 | |||
49 | public override string ConfigName | ||
50 | { | ||
51 | get { return "RestRegionPlugin"; } | ||
52 | } | ||
53 | #endregion overriding properties | ||
54 | |||
55 | #region overriding methods | ||
56 | /// <summary> | ||
57 | /// This method is called by OpenSimMain immediately after loading the | ||
58 | /// plugin and after basic server setup, but before running any server commands. | ||
59 | /// </summary> | ||
60 | /// <remarks> | ||
61 | /// Note that entries MUST be added to the active configuration files before | ||
62 | /// the plugin can be enabled. | ||
63 | /// </remarks> | ||
64 | public override void Initialise(OpenSimBase openSim) | ||
65 | { | ||
66 | try | ||
67 | { | ||
68 | base.Initialise(openSim); | ||
69 | if (!IsEnabled) | ||
70 | { | ||
71 | //m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID); | ||
72 | return; | ||
73 | } | ||
74 | |||
75 | m_log.InfoFormat("{0} REST region plugin enabled", MsgID); | ||
76 | |||
77 | // add REST method handlers | ||
78 | AddRestStreamHandler("GET", "/regions/", GetHandler); | ||
79 | AddRestStreamHandler("POST", "/regions/", PostHandler); | ||
80 | AddRestStreamHandler("GET", "/regioninfo/", GetRegionInfoHandler); | ||
81 | } | ||
82 | catch (Exception e) | ||
83 | { | ||
84 | m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message); | ||
85 | m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString()); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | public override void Close() | ||
90 | { | ||
91 | } | ||
92 | #endregion overriding methods | ||
93 | } | ||
94 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/RestPlugin.cs b/OpenSim/ApplicationPlugins/Rest/RestPlugin.cs deleted file mode 100644 index a2425b5..0000000 --- a/OpenSim/ApplicationPlugins/Rest/RestPlugin.cs +++ /dev/null | |||
@@ -1,417 +0,0 @@ | |||
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.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | using System.Xml; | ||
33 | using log4net; | ||
34 | using Nini.Config; | ||
35 | using OpenMetaverse; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Framework.Servers; | ||
38 | using OpenSim.Framework.Servers.HttpServer; | ||
39 | |||
40 | namespace OpenSim.ApplicationPlugins.Rest | ||
41 | { | ||
42 | public abstract class RestPlugin : IApplicationPlugin | ||
43 | { | ||
44 | #region properties | ||
45 | |||
46 | protected static readonly ILog m_log = | ||
47 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
48 | |||
49 | private IConfig _config; // Configuration source: Rest Plugins | ||
50 | private IConfig _pluginConfig; // Configuration source: Plugin specific | ||
51 | private OpenSimBase _app; // The 'server' | ||
52 | private BaseHttpServer _httpd; // The server's RPC interface | ||
53 | private string _prefix; // URL prefix below | ||
54 | // which all REST URLs | ||
55 | // are living | ||
56 | // private StringWriter _sw = null; | ||
57 | // private RestXmlWriter _xw = null; | ||
58 | |||
59 | private string _godkey; | ||
60 | private int _reqk; | ||
61 | |||
62 | [ThreadStatic] | ||
63 | private static string _threadRequestID = String.Empty; | ||
64 | |||
65 | /// <summary> | ||
66 | /// Return an ever increasing request ID for logging | ||
67 | /// </summary> | ||
68 | protected string RequestID | ||
69 | { | ||
70 | get { return _reqk++.ToString(); } | ||
71 | set { _reqk = Convert.ToInt32(value); } | ||
72 | } | ||
73 | |||
74 | /// <summary> | ||
75 | /// Thread-constant message IDs for logging. | ||
76 | /// </summary> | ||
77 | protected string MsgID | ||
78 | { | ||
79 | get { return String.Format("[REST-{0}] #{1}", Name, _threadRequestID); } | ||
80 | set { _threadRequestID = value; } | ||
81 | } | ||
82 | |||
83 | /// <summary> | ||
84 | /// Returns true if Rest Plugins are enabled. | ||
85 | /// </summary> | ||
86 | public bool PluginsAreEnabled | ||
87 | { | ||
88 | get { return null != _config; } | ||
89 | } | ||
90 | |||
91 | /// <summary> | ||
92 | /// Returns true if specific Rest Plugin is enabled. | ||
93 | /// </summary> | ||
94 | public bool IsEnabled | ||
95 | { | ||
96 | get | ||
97 | { | ||
98 | return (null != _pluginConfig) && _pluginConfig.GetBoolean("enabled", false); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | /// <summary> | ||
103 | /// OpenSimMain application | ||
104 | /// </summary> | ||
105 | public OpenSimBase App | ||
106 | { | ||
107 | get { return _app; } | ||
108 | } | ||
109 | |||
110 | /// <summary> | ||
111 | /// RPC server | ||
112 | /// </summary> | ||
113 | public BaseHttpServer HttpServer | ||
114 | { | ||
115 | get { return _httpd; } | ||
116 | } | ||
117 | |||
118 | /// <summary> | ||
119 | /// URL prefix to use for all REST handlers | ||
120 | /// </summary> | ||
121 | public string Prefix | ||
122 | { | ||
123 | get { return _prefix; } | ||
124 | } | ||
125 | |||
126 | /// <summary> | ||
127 | /// Access to GOD password string | ||
128 | /// </summary> | ||
129 | protected string GodKey | ||
130 | { | ||
131 | get { return _godkey; } | ||
132 | } | ||
133 | |||
134 | /// <summary> | ||
135 | /// Configuration of the plugin | ||
136 | /// </summary> | ||
137 | public IConfig Config | ||
138 | { | ||
139 | get { return _pluginConfig; } | ||
140 | } | ||
141 | |||
142 | /// <summary> | ||
143 | /// Name of the plugin | ||
144 | /// </summary> | ||
145 | public abstract string Name { get; } | ||
146 | |||
147 | /// <summary> | ||
148 | /// Return the config section name | ||
149 | /// </summary> | ||
150 | public abstract string ConfigName { get; } | ||
151 | |||
152 | // public XmlTextWriter XmlWriter | ||
153 | // { | ||
154 | // get | ||
155 | // { | ||
156 | // if (null == _xw) | ||
157 | // { | ||
158 | // _sw = new StringWriter(); | ||
159 | // _xw = new RestXmlWriter(_sw); | ||
160 | // _xw.Formatting = Formatting.Indented; | ||
161 | // } | ||
162 | // return _xw; | ||
163 | // } | ||
164 | // } | ||
165 | |||
166 | // public string XmlWriterResult | ||
167 | // { | ||
168 | // get | ||
169 | // { | ||
170 | // _xw.Flush(); | ||
171 | // _xw.Close(); | ||
172 | // _xw = null; | ||
173 | |||
174 | // return _sw.ToString(); | ||
175 | // } | ||
176 | // } | ||
177 | |||
178 | #endregion properties | ||
179 | |||
180 | #region methods | ||
181 | |||
182 | // TODO: required by IPlugin, but likely not at all right | ||
183 | private string m_version = "0.0"; | ||
184 | |||
185 | public string Version | ||
186 | { | ||
187 | get { return m_version; } | ||
188 | } | ||
189 | |||
190 | public void Initialise() | ||
191 | { | ||
192 | m_log.Info("[RESTPLUGIN]: " + Name + " cannot be default-initialized!"); | ||
193 | throw new PluginNotInitialisedException(Name); | ||
194 | } | ||
195 | |||
196 | /// <summary> | ||
197 | /// This method is called by OpenSimMain immediately after loading the | ||
198 | /// plugin and after basic server setup, but before running any server commands. | ||
199 | /// </summary> | ||
200 | /// <remarks> | ||
201 | /// Note that entries MUST be added to the active configuration files before | ||
202 | /// the plugin can be enabled. | ||
203 | /// </remarks> | ||
204 | public virtual void Initialise(OpenSimBase openSim) | ||
205 | { | ||
206 | RequestID = "0"; | ||
207 | MsgID = RequestID; | ||
208 | |||
209 | try | ||
210 | { | ||
211 | if ((_config = openSim.ConfigSource.Source.Configs["RestPlugins"]) == null) | ||
212 | { | ||
213 | m_log.WarnFormat("{0} Rest Plugins not configured", MsgID); | ||
214 | return; | ||
215 | } | ||
216 | |||
217 | if (!_config.GetBoolean("enabled", false)) | ||
218 | { | ||
219 | //m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | _app = openSim; | ||
224 | _httpd = openSim.HttpServer; | ||
225 | |||
226 | // Retrieve GOD key value, if any. | ||
227 | _godkey = _config.GetString("god_key", String.Empty); | ||
228 | |||
229 | // Retrive prefix if any. | ||
230 | _prefix = _config.GetString("prefix", "/admin"); | ||
231 | |||
232 | // Get plugin specific config | ||
233 | _pluginConfig = openSim.ConfigSource.Source.Configs[ConfigName]; | ||
234 | |||
235 | m_log.InfoFormat("{0} Rest Plugins Enabled", MsgID); | ||
236 | } | ||
237 | catch (Exception e) | ||
238 | { | ||
239 | // we can safely ignore this, as it just means that | ||
240 | // the key lookup in Configs failed, which signals to | ||
241 | // us that noone is interested in our services...they | ||
242 | // don't know what they are missing out on... | ||
243 | // NOTE: Under the present OpenSimulator implementation it is | ||
244 | // not possible for the openSimulator pointer to be null. However | ||
245 | // were the implementation to be changed, this could | ||
246 | // result in a silent initialization failure. Harmless | ||
247 | // except for lack of function and lack of any | ||
248 | // diagnostic indication as to why. The same is true if | ||
249 | // the HTTP server reference is bad. | ||
250 | // We should at least issue a message... | ||
251 | m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message); | ||
252 | m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString()); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | public virtual void PostInitialise() | ||
257 | { | ||
258 | } | ||
259 | |||
260 | private List<RestStreamHandler> _handlers = new List<RestStreamHandler>(); | ||
261 | private Dictionary<string, IHttpAgentHandler> _agents = new Dictionary<string, IHttpAgentHandler>(); | ||
262 | |||
263 | /// <summary> | ||
264 | /// Add a REST stream handler to the underlying HTTP server. | ||
265 | /// </summary> | ||
266 | /// <param name="httpMethod">GET/PUT/POST/DELETE or | ||
267 | /// similar</param> | ||
268 | /// <param name="path">URL prefix</param> | ||
269 | /// <param name="method">RestMethod handler doing the actual work</param> | ||
270 | public virtual void AddRestStreamHandler(string httpMethod, string path, RestMethod method) | ||
271 | { | ||
272 | if (!IsEnabled) return; | ||
273 | |||
274 | if (!path.StartsWith(_prefix)) | ||
275 | { | ||
276 | path = String.Format("{0}{1}", _prefix, path); | ||
277 | } | ||
278 | |||
279 | RestStreamHandler h = new RestStreamHandler(httpMethod, path, method); | ||
280 | _httpd.AddStreamHandler(h); | ||
281 | _handlers.Add(h); | ||
282 | |||
283 | m_log.DebugFormat("{0} Added REST handler {1} {2}", MsgID, httpMethod, path); | ||
284 | } | ||
285 | |||
286 | /// <summary> | ||
287 | /// Add a powerful Agent handler to the underlying HTTP | ||
288 | /// server. | ||
289 | /// </summary> | ||
290 | /// <param name="agentName">name of agent handler</param> | ||
291 | /// <param name="handler">agent handler method</param> | ||
292 | /// <returns>false when the plugin is disabled or the agent | ||
293 | /// handler could not be added. Any generated exceptions are | ||
294 | /// allowed to drop through to the caller, i.e. ArgumentException. | ||
295 | /// </returns> | ||
296 | public bool AddAgentHandler(string agentName, IHttpAgentHandler handler) | ||
297 | { | ||
298 | if (!IsEnabled) return false; | ||
299 | _agents.Add(agentName, handler); | ||
300 | // return _httpd.AddAgentHandler(agentName, handler); | ||
301 | |||
302 | return false; | ||
303 | } | ||
304 | |||
305 | /// <summary> | ||
306 | /// Remove a powerful Agent handler from the underlying HTTP | ||
307 | /// server. | ||
308 | /// </summary> | ||
309 | /// <param name="agentName">name of agent handler</param> | ||
310 | /// <param name="handler">agent handler method</param> | ||
311 | /// <returns>false when the plugin is disabled or the agent | ||
312 | /// handler could not be removed. Any generated exceptions are | ||
313 | /// allowed to drop through to the caller, i.e. KeyNotFound. | ||
314 | /// </returns> | ||
315 | public bool RemoveAgentHandler(string agentName, IHttpAgentHandler handler) | ||
316 | { | ||
317 | if (!IsEnabled) return false; | ||
318 | if (_agents[agentName] == handler) | ||
319 | { | ||
320 | _agents.Remove(agentName); | ||
321 | // return _httpd.RemoveAgentHandler(agentName, handler); | ||
322 | } | ||
323 | return false; | ||
324 | } | ||
325 | |||
326 | /// <summary> | ||
327 | /// Check whether the HTTP request came from god; that is, is | ||
328 | /// the god_key as configured in the config section supplied | ||
329 | /// via X-OpenSim-Godkey? | ||
330 | /// </summary> | ||
331 | /// <param name="request">HTTP request header</param> | ||
332 | /// <returns>true when the HTTP request came from god.</returns> | ||
333 | protected bool IsGod(IOSHttpRequest request) | ||
334 | { | ||
335 | string[] keys = request.Headers.GetValues("X-OpenSim-Godkey"); | ||
336 | if (null == keys) return false; | ||
337 | |||
338 | // we take the last key supplied | ||
339 | return keys[keys.Length - 1] == _godkey; | ||
340 | } | ||
341 | |||
342 | /// <summary> | ||
343 | /// Checks wether the X-OpenSim-Password value provided in the | ||
344 | /// HTTP header is indeed the password on file for the avatar | ||
345 | /// specified by the UUID | ||
346 | /// </summary> | ||
347 | protected bool IsVerifiedUser(IOSHttpRequest request, UUID uuid) | ||
348 | { | ||
349 | // XXX under construction | ||
350 | return false; | ||
351 | } | ||
352 | |||
353 | /// <summary> | ||
354 | /// Clean up and remove all handlers that were added earlier. | ||
355 | /// </summary> | ||
356 | public virtual void Close() | ||
357 | { | ||
358 | foreach (RestStreamHandler h in _handlers) | ||
359 | { | ||
360 | _httpd.RemoveStreamHandler(h.HttpMethod, h.Path); | ||
361 | } | ||
362 | _handlers = null; | ||
363 | // foreach (KeyValuePair<string, IHttpAgentHandler> h in _agents) | ||
364 | // { | ||
365 | // _httpd.RemoveAgentHandler(h.Key, h.Value); | ||
366 | // } | ||
367 | _agents = null; | ||
368 | } | ||
369 | |||
370 | public virtual void Dispose() | ||
371 | { | ||
372 | Close(); | ||
373 | } | ||
374 | |||
375 | /// <summary> | ||
376 | /// Return a failure message. | ||
377 | /// </summary> | ||
378 | /// <param name="method">origin of the failure message</param> | ||
379 | /// <param name="message">failure message</param> | ||
380 | /// <remarks>This should probably set a return code as | ||
381 | /// well. (?)</remarks> | ||
382 | protected string Failure(IOSHttpResponse response, OSHttpStatusCode status, | ||
383 | string method, string format, params string[] msg) | ||
384 | { | ||
385 | string m = String.Format(format, msg); | ||
386 | |||
387 | response.StatusCode = (int) status; | ||
388 | response.StatusDescription = m; | ||
389 | |||
390 | m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, m); | ||
391 | return String.Format("<error>{0}</error>", m); | ||
392 | } | ||
393 | |||
394 | /// <summary> | ||
395 | /// Return a failure message. | ||
396 | /// </summary> | ||
397 | /// <param name="method">origin of the failure message</param> | ||
398 | /// <param name="e">exception causing the failure message</param> | ||
399 | /// <remarks>This should probably set a return code as | ||
400 | /// well. (?)</remarks> | ||
401 | public string Failure(IOSHttpResponse response, OSHttpStatusCode status, | ||
402 | string method, Exception e) | ||
403 | { | ||
404 | string m = String.Format("exception occurred: {0}", e.Message); | ||
405 | |||
406 | response.StatusCode = (int) status; | ||
407 | response.StatusDescription = m; | ||
408 | |||
409 | m_log.DebugFormat("{0} {1} failed: {2}", MsgID, method, e.ToString()); | ||
410 | m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, e.Message); | ||
411 | |||
412 | return String.Format("<error>{0}</error>", e.Message); | ||
413 | } | ||
414 | |||
415 | #endregion methods | ||
416 | } | ||
417 | } | ||
diff --git a/OpenSim/ApplicationPlugins/Rest/rest.xsd b/OpenSim/ApplicationPlugins/Rest/rest.xsd deleted file mode 100644 index 4dc0ae4..0000000 --- a/OpenSim/ApplicationPlugins/Rest/rest.xsd +++ /dev/null | |||
@@ -1,276 +0,0 @@ | |||
1 | <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> | ||
2 | |||
3 | <xsd:annotation> | ||
4 | <xsd:documentation xml:lang="en"> | ||
5 | Open Simulator Export/Import XML schema | ||
6 | August 2008 | ||
7 | </xsd:documentation> | ||
8 | </xsd:annotation> | ||
9 | |||
10 | <!-- WARNING!!! | ||
11 | This is currently a draft, it does not reflect | ||
12 | what is exported, nor what will be understood | ||
13 | on import. It is included as a working document | ||
14 | and this comment will be removed at such time as | ||
15 | the schema corresponds to reality. | ||
16 | --> | ||
17 | |||
18 | <!-- | ||
19 | REST-related information | ||
20 | Inventory data is always framed by an | ||
21 | inventory element. Consists of zero or | ||
22 | more elements representing either folders | ||
23 | or items within those folders. The inventory | ||
24 | element represents the "real" root folder. | ||
25 | --> | ||
26 | |||
27 | <xsd:element name="inventory" type="inventory_ct" /> | ||
28 | |||
29 | <!-- | ||
30 | The inventory complex type is just an arbitrary | ||
31 | sequence of folders and items. In reality it is | ||
32 | typically just folders. Both item and folder | ||
33 | have corresponding complex types. It is distinct | ||
34 | from folders insofar as it has no other defining | ||
35 | attributes. | ||
36 | --> | ||
37 | |||
38 | <xsd:complexType name="inventory_ct"> | ||
39 | <xsd:element name="folder" type="folder_ct" maxOccurs="unbounded"/> | ||
40 | <xsd:element name="item" type="item_ct" maxOccurs="unbounded" /> | ||
41 | </xsd:complexType> | ||
42 | |||
43 | <xsd:complexType name="folder_ct"> | ||
44 | <xsd:attribute name="UUID" type="uuid_st" /> | ||
45 | <xsd:attribute name="name" type="name_st" /> | ||
46 | <xsd:attribute name="type" type="folder_type_st" /> | ||
47 | <xsd:attribute name="description" type="xsd:string" /> <!-- added --> | ||
48 | <xsd:attribute name="version" type="unsignedShort" /> | ||
49 | <xsd:attribute name="owner" type="uuid_st" /> | ||
50 | |||
51 | <xsd:attribute name="creator" type="uuid_st" /> <!-- added --> | ||
52 | <xsd:attribute name="creationdate" type="date_st" /> <!-- added --> | ||
53 | |||
54 | <xsd:attribute name="parent" type="uuid_st" /> | ||
55 | |||
56 | <xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" /> <!-- added --> | ||
57 | <xsd:element name="folder" type="folder_ct" maxOccurs="unbounded" /> | ||
58 | <xsd:element name="item" type="item_ct" maxOccurs="unbounded" /> | ||
59 | </xsd:complexType> | ||
60 | |||
61 | <xsd:complexType name="item_ct"> | ||
62 | <xsd:attribute name="UUID" type="uuid_st" /> | ||
63 | <xsd:attribute name="name" type="name_st" /> | ||
64 | <xsd:attribute name="type" type="inventory_type_st" /> | ||
65 | <xsd:attribute name="description" type="xsd:string" /> | ||
66 | <xsd:attribute name="version" type="unsignedShort" /> <!-- added --> | ||
67 | <xsd:attribute name="owner" type="uuid_st" /> | ||
68 | |||
69 | <xsd:attribute name="creator" type="uuid_st" /> | ||
70 | <xsd:attribute name="creationdate" type="date_st" /> | ||
71 | |||
72 | <xsd:attribute name="folder" type="uuid_st" /> | ||
73 | <xsd:attribute name="groupid" type="uuid_st" /> | ||
74 | <xsd:attribute name="groupowned" type="xsd:boolean" /> | ||
75 | <xsd:attribute name="saletype" type="sale_st" /> | ||
76 | <xsd:attribute name="saleprice" type="xsd:decimal" /> | ||
77 | |||
78 | <xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" /> | ||
79 | </xsd:complexType> | ||
80 | |||
81 | <xsd:complexType name="asset_ct"> | ||
82 | <xsd:attribute name="UUID" type="uuid_st" /> | ||
83 | <xsd:attribute name="name" type="name_st" /> | ||
84 | <xsd:attribute name="type" type="asset_type_st" /> | ||
85 | <xsd:attribute name="description" type="xsd:string" /> | ||
86 | <xsd:attribute name="version" type="unsignedShort" /> <!-- added --> | ||
87 | <xsd:attribute name="owner" type="uuid_st" /> | ||
88 | |||
89 | <xsd:attribute name="creator" type="uuid_st" /> | ||
90 | <xsd:attribute name="creationdate" type="date_st" /> | ||
91 | |||
92 | <xsd:attribute name="temporary" type="xsd:boolean" /> | ||
93 | <xsd:attribute name="local" type="xsd:boolean" /> | ||
94 | <xsd:attribute name="inline" type="xsd:boolean" /> | ||
95 | </xsd:complexType> | ||
96 | |||
97 | <!-- Constrained Simple Data types --> | ||
98 | |||
99 | <!-- | ||
100 | We need to specify name as a simple type because on | ||
101 | some platforms it is constrained by a certain length | ||
102 | limitation. For completeness we indicate that whitespace | ||
103 | should be preserved exactly as specified. | ||
104 | --> | ||
105 | |||
106 | <xsd:simpleType name="name_st"> | ||
107 | <xsd:restriction base="xsd:string"> | ||
108 | <whiteSpace value="preserve" /> | ||
109 | <minLength value="0" /> | ||
110 | <maxLength value="64" /> | ||
111 | </xsd:restriction> | ||
112 | </xsd:simpleType> | ||
113 | |||
114 | <!-- | ||
115 | Type information in the folder is meant to indicate | ||
116 | the preferred asset type for this folder. As such, that | ||
117 | currently corresponds to the type values allowed for | ||
118 | assets, however that is not mandated, so for | ||
119 | now at least I'll represent this as a distinct | ||
120 | enumeration. | ||
121 | This seems inappropriate; it seems like the folder's | ||
122 | content should reflect the InventoryType classifications | ||
123 | rather than the asset types. | ||
124 | --> | ||
125 | |||
126 | <xsd:simpleType name="folder_type_st"> | ||
127 | <xsd:restriction base="xsd:string"> | ||
128 | <xsd:enumeration value="Texture" /> | ||
129 | <xsd:enumeration value="Sound" /> | ||
130 | <xsd:enumeration value="CallingCard" /> | ||
131 | <xsd:enumeration value="Landmark" /> | ||
132 | <xsd:enumeration value="Script" /> | ||
133 | <xsd:enumeration value="Clothing" /> | ||
134 | <xsd:enumeration value="Object" /> | ||
135 | <xsd:enumeration value="Notecard" /> | ||
136 | <xsd:enumeration value="LSLText" /> | ||
137 | <xsd:enumeration value="LSLByteCode" /> | ||
138 | <xsd:enumeration value="TextureTGA" /> | ||
139 | <xsd:enumeration value="BodyPart" /> | ||
140 | <xsd:enumeration value="SoundWAV" /> | ||
141 | <xsd:enumeration value="ImageTGA" /> | ||
142 | <xsd:enumeration value="ImageJPEG" /> | ||
143 | <xsd:enumeration value="Animation" /> | ||
144 | <xsd:enumeration value="Gesture" /> | ||
145 | <xsd:enumeration value="Simstate" /> | ||
146 | <xsd:enumeration value="Unknown" /> | ||
147 | <xsd:enumeration value="LostAndFoundFolder" /> | ||
148 | <xsd:enumeration value="SnapshotFolder" /> | ||
149 | <xsd:enumeration value="TrashFolder" /> | ||
150 | <xsd:enumeration value="Folder" /> | ||
151 | <xsd:enumeration value="RootFolder" /> | ||
152 | </xsd:restriction> | ||
153 | </xsd:simpleType> | ||
154 | |||
155 | <!-- | ||
156 | Inventory item type designates an asset class, rather | ||
157 | than a specific asset type. For example, "SnapShot" | ||
158 | might include a number of asset types such as JPEG, | ||
159 | TGA, etc.. This is not a consistent interpretation, | ||
160 | classifications such as LostAndFound are meta-types | ||
161 | relative to asset classes. | ||
162 | |||
163 | These types should be abstract and not be tied to a | ||
164 | specific platform. A world's import facility should be | ||
165 | responsible for mapping these to meaningful internal | ||
166 | representations. | ||
167 | |||
168 | These types were based on information in: | ||
169 | libsecondlife/InventoryManager.cs | ||
170 | --> | ||
171 | |||
172 | <xsd:simpleType name="inventory_type_st"> | ||
173 | <xsd:restriction base="xsd:string"> | ||
174 | <xsd:enumeration value="Texture" /> | ||
175 | <xsd:enumeration value="Sound" /> | ||
176 | <xsd:enumeration value="CallingCard" /> | ||
177 | <xsd:enumeration value="Landmark" /> | ||
178 | <xsd:enumeration value="Script" /> | ||
179 | <xsd:enumeration value="Clothing" /> | ||
180 | <xsd:enumeration value="Object" /> | ||
181 | <xsd:enumeration value="Notecard" /> | ||
182 | <xsd:enumeration value="LSL" /> | ||
183 | <xsd:enumeration value="LSLBytecode" /> | ||
184 | <xsd:enumeration value="TextureTGA" /> | ||
185 | <xsd:enumeration value="BodyPart" /> | ||
186 | <xsd:enumeration value="Snapshot" /> | ||
187 | <xsd:enumeration value="Attachment" /> | ||
188 | <xsd:enumeration value="Wearable" /> | ||
189 | <xsd:enumeration value="Animation" /> | ||
190 | <xsd:enumeration value="Gesture" /> | ||
191 | <xsd:enumeration value="Folder" /> | ||
192 | <xsd:enumeration value="Unknown" /> | ||
193 | <xsd:enumeration value="LostAndFound" /> | ||
194 | <xsd:enumeration value="Trash" /> | ||
195 | <xsd:enumeration value="Root" /> | ||
196 | </xsd:restriction> | ||
197 | </xsd:simpleType> | ||
198 | |||
199 | <!-- | ||
200 | The asset types seem to be even more disarrayed than | ||
201 | the inventory types. It seems to be little more than | ||
202 | a reiteration of the inventory type information, | ||
203 | which adds little or nothing to the overall data | ||
204 | model. | ||
205 | |||
206 | Of course, given that these are drawn from the | ||
207 | libsecondlife definitions, we aren't at liberty to | ||
208 | simply redefine them in place. But the XML definitions | ||
209 | here could be made more useful. | ||
210 | |||
211 | These types were based on information in: | ||
212 | libsecondlife/AssetManager.cs | ||
213 | --> | ||
214 | |||
215 | <xsd:simpleType name="asset_type_st"> | ||
216 | <xsd:restriction base="xsd:string"> | ||
217 | <xsd:enumeration value="Texture" /> | ||
218 | <xsd:enumeration value="Sound" /> | ||
219 | <xsd:enumeration value="CallingCard" /> | ||
220 | <xsd:enumeration value="Landmark" /> | ||
221 | <xsd:enumeration value="Script" /> | ||
222 | <xsd:enumeration value="Clothing" /> | ||
223 | <xsd:enumeration value="Object" /> | ||
224 | <xsd:enumeration value="Notecard" /> | ||
225 | <xsd:enumeration value="LSLText" /> | ||
226 | <xsd:enumeration value="LSLByteCode" /> | ||
227 | <xsd:enumeration value="TextureTGA" /> | ||
228 | <xsd:enumeration value="BodyPart" /> | ||
229 | <xsd:enumeration value="SoundWAV" /> | ||
230 | <xsd:enumeration value="ImageTGA" /> | ||
231 | <xsd:enumeration value="ImageJPEG" /> | ||
232 | <xsd:enumeration value="Animation" /> | ||
233 | <xsd:enumeration value="Gesture" /> | ||
234 | <xsd:enumeration value="Simstate" /> | ||
235 | <xsd:enumeration value="Unknown" /> | ||
236 | <xsd:enumeration value="LostAndFoundFolder" /> | ||
237 | <xsd:enumeration value="SnapshotFolder" /> | ||
238 | <xsd:enumeration value="TrashFolder" /> | ||
239 | <xsd:enumeration value="Folder" /> | ||
240 | <xsd:enumeration value="RootFolder" /> | ||
241 | </xsd:restriction> | ||
242 | </xsd:simpleType> | ||
243 | |||
244 | <!-- This is describing the apparent form of a UUID. If | ||
245 | we ever want a more metaphysical definition we'll | ||
246 | need to add to it. | ||
247 | --> | ||
248 | |||
249 | <xsd:simpleType name="uuid_st"> | ||
250 | <xsd:restriction base="xsd:string"> | ||
251 | <xsd:pattern value="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"/> | ||
252 | </xsd:restriction> | ||
253 | </xsd:simpleType> | ||
254 | |||
255 | <!-- This constrains the date representation. Currently | ||
256 | it is simply an integer representing the elapsed | ||
257 | ?? since ??. | ||
258 | --> | ||
259 | |||
260 | <xsd:simpleType name="date_st"> | ||
261 | <xsd:restriction base="xsd:positiveInteger"> | ||
262 | </xsd:restriction> | ||
263 | </xsd:simpleType> | ||
264 | |||
265 | <!-- This constrains the representation of sale price. | ||
266 | Currently it is a simple decimal with no unit | ||
267 | specified. | ||
268 | Issues: interoperability. | ||
269 | --> | ||
270 | |||
271 | <xsd:simpleType name="sale_st"> | ||
272 | <xsd:restriction base="xsd:decimal"> | ||
273 | </xsd:restriction> | ||
274 | </xsd:simpleType> | ||
275 | |||
276 | </xsd:schema> | ||
diff --git a/OpenSim/Data/MySQL/MySQLXAssetData.cs b/OpenSim/Data/MySQL/MySQLXAssetData.cs index c2282c8..8c93825 100644 --- a/OpenSim/Data/MySQL/MySQLXAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLXAssetData.cs | |||
@@ -50,6 +50,11 @@ namespace OpenSim.Data.MySQL | |||
50 | get { return GetType().Assembly; } | 50 | get { return GetType().Assembly; } |
51 | } | 51 | } |
52 | 52 | ||
53 | /// <summary> | ||
54 | /// Number of days that must pass before we update the access time on an asset when it has been fetched. | ||
55 | /// </summary> | ||
56 | private const int DaysBetweenAccessTimeUpdates = 30; | ||
57 | |||
53 | private bool m_enableCompression = false; | 58 | private bool m_enableCompression = false; |
54 | private string m_connectionString; | 59 | private string m_connectionString; |
55 | private object m_dbLock = new object(); | 60 | private object m_dbLock = new object(); |
@@ -133,10 +138,10 @@ namespace OpenSim.Data.MySQL | |||
133 | dbcon.Open(); | 138 | dbcon.Open(); |
134 | 139 | ||
135 | using (MySqlCommand cmd = new MySqlCommand( | 140 | using (MySqlCommand cmd = new MySqlCommand( |
136 | "SELECT name, description, asset_type, local, temporary, asset_flags, creator_id, data FROM xassetsmeta JOIN xassetsdata ON xassetsmeta.hash = xassetsdata.hash WHERE id=?id", | 141 | "SELECT Name, Description, AccessTime, AssetType, Local, Temporary, AssetFlags, CreatorID, Data FROM XAssetsMeta JOIN XAssetsData ON XAssetsMeta.Hash = XAssetsData.Hash WHERE ID=?ID", |
137 | dbcon)) | 142 | dbcon)) |
138 | { | 143 | { |
139 | cmd.Parameters.AddWithValue("?id", assetID.ToString()); | 144 | cmd.Parameters.AddWithValue("?ID", assetID.ToString()); |
140 | 145 | ||
141 | try | 146 | try |
142 | { | 147 | { |
@@ -144,18 +149,18 @@ namespace OpenSim.Data.MySQL | |||
144 | { | 149 | { |
145 | if (dbReader.Read()) | 150 | if (dbReader.Read()) |
146 | { | 151 | { |
147 | asset = new AssetBase(assetID, (string)dbReader["name"], (sbyte)dbReader["asset_type"], dbReader["creator_id"].ToString()); | 152 | asset = new AssetBase(assetID, (string)dbReader["Name"], (sbyte)dbReader["AssetType"], dbReader["CreatorID"].ToString()); |
148 | asset.Data = (byte[])dbReader["data"]; | 153 | asset.Data = (byte[])dbReader["Data"]; |
149 | asset.Description = (string)dbReader["description"]; | 154 | asset.Description = (string)dbReader["Description"]; |
150 | 155 | ||
151 | string local = dbReader["local"].ToString(); | 156 | string local = dbReader["Local"].ToString(); |
152 | if (local.Equals("1") || local.Equals("true", StringComparison.InvariantCultureIgnoreCase)) | 157 | if (local.Equals("1") || local.Equals("true", StringComparison.InvariantCultureIgnoreCase)) |
153 | asset.Local = true; | 158 | asset.Local = true; |
154 | else | 159 | else |
155 | asset.Local = false; | 160 | asset.Local = false; |
156 | 161 | ||
157 | asset.Temporary = Convert.ToBoolean(dbReader["temporary"]); | 162 | asset.Temporary = Convert.ToBoolean(dbReader["Temporary"]); |
158 | asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); | 163 | asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["AssetFlags"]); |
159 | 164 | ||
160 | if (m_enableCompression) | 165 | if (m_enableCompression) |
161 | { | 166 | { |
@@ -171,12 +176,14 @@ namespace OpenSim.Data.MySQL | |||
171 | // asset.ID, asset.Name, asset.Data.Length, compressedLength); | 176 | // asset.ID, asset.Name, asset.Data.Length, compressedLength); |
172 | } | 177 | } |
173 | } | 178 | } |
179 | |||
180 | UpdateAccessTime(asset.Metadata, (int)dbReader["AccessTime"]); | ||
174 | } | 181 | } |
175 | } | 182 | } |
176 | } | 183 | } |
177 | catch (Exception e) | 184 | catch (Exception e) |
178 | { | 185 | { |
179 | m_log.Error("[MYSQL XASSET DATA]: MySql failure fetching asset " + assetID + ": " + e.Message); | 186 | m_log.Error(string.Format("[MYSQL XASSET DATA]: Failure fetching asset {0}", assetID), e); |
180 | } | 187 | } |
181 | } | 188 | } |
182 | } | 189 | } |
@@ -242,23 +249,23 @@ namespace OpenSim.Data.MySQL | |||
242 | { | 249 | { |
243 | using (MySqlCommand cmd = | 250 | using (MySqlCommand cmd = |
244 | new MySqlCommand( | 251 | new MySqlCommand( |
245 | "replace INTO xassetsmeta(id, hash, name, description, asset_type, local, temporary, create_time, access_time, asset_flags, creator_id)" + | 252 | "replace INTO XAssetsMeta(ID, Hash, Name, Description, AssetType, Local, Temporary, CreateTime, AccessTime, AssetFlags, CreatorID)" + |
246 | "VALUES(?id, ?hash, ?name, ?description, ?asset_type, ?local, ?temporary, ?create_time, ?access_time, ?asset_flags, ?creator_id)", | 253 | "VALUES(?ID, ?Hash, ?Name, ?Description, ?AssetType, ?Local, ?Temporary, ?CreateTime, ?AccessTime, ?AssetFlags, ?CreatorID)", |
247 | dbcon)) | 254 | dbcon)) |
248 | { | 255 | { |
249 | // create unix epoch time | 256 | // create unix epoch time |
250 | int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); | 257 | int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); |
251 | cmd.Parameters.AddWithValue("?id", asset.ID); | 258 | cmd.Parameters.AddWithValue("?ID", asset.ID); |
252 | cmd.Parameters.AddWithValue("?hash", hash); | 259 | cmd.Parameters.AddWithValue("?Hash", hash); |
253 | cmd.Parameters.AddWithValue("?name", assetName); | 260 | cmd.Parameters.AddWithValue("?Name", assetName); |
254 | cmd.Parameters.AddWithValue("?description", assetDescription); | 261 | cmd.Parameters.AddWithValue("?Description", assetDescription); |
255 | cmd.Parameters.AddWithValue("?asset_type", asset.Type); | 262 | cmd.Parameters.AddWithValue("?AssetType", asset.Type); |
256 | cmd.Parameters.AddWithValue("?local", asset.Local); | 263 | cmd.Parameters.AddWithValue("?Local", asset.Local); |
257 | cmd.Parameters.AddWithValue("?temporary", asset.Temporary); | 264 | cmd.Parameters.AddWithValue("?Temporary", asset.Temporary); |
258 | cmd.Parameters.AddWithValue("?create_time", now); | 265 | cmd.Parameters.AddWithValue("?CreateTime", now); |
259 | cmd.Parameters.AddWithValue("?access_time", now); | 266 | cmd.Parameters.AddWithValue("?AccessTime", now); |
260 | cmd.Parameters.AddWithValue("?creator_id", asset.Metadata.CreatorID); | 267 | cmd.Parameters.AddWithValue("?CreatorID", asset.Metadata.CreatorID); |
261 | cmd.Parameters.AddWithValue("?asset_flags", (int)asset.Flags); | 268 | cmd.Parameters.AddWithValue("?AssetFlags", (int)asset.Flags); |
262 | cmd.ExecuteNonQuery(); | 269 | cmd.ExecuteNonQuery(); |
263 | } | 270 | } |
264 | } | 271 | } |
@@ -278,11 +285,11 @@ namespace OpenSim.Data.MySQL | |||
278 | { | 285 | { |
279 | using (MySqlCommand cmd = | 286 | using (MySqlCommand cmd = |
280 | new MySqlCommand( | 287 | new MySqlCommand( |
281 | "INSERT INTO xassetsdata(hash, data) VALUES(?hash, ?data)", | 288 | "INSERT INTO XAssetsData(Hash, Data) VALUES(?Hash, ?Data)", |
282 | dbcon)) | 289 | dbcon)) |
283 | { | 290 | { |
284 | cmd.Parameters.AddWithValue("?hash", hash); | 291 | cmd.Parameters.AddWithValue("?Hash", hash); |
285 | cmd.Parameters.AddWithValue("?data", asset.Data); | 292 | cmd.Parameters.AddWithValue("?Data", asset.Data); |
286 | cmd.ExecuteNonQuery(); | 293 | cmd.ExecuteNonQuery(); |
287 | } | 294 | } |
288 | } | 295 | } |
@@ -303,41 +310,49 @@ namespace OpenSim.Data.MySQL | |||
303 | } | 310 | } |
304 | } | 311 | } |
305 | 312 | ||
306 | // private void UpdateAccessTime(AssetBase asset) | 313 | /// <summary> |
307 | // { | 314 | /// Updates the access time of the asset if it was accessed above a given threshhold amount of time. |
308 | // lock (m_dbLock) | 315 | /// </summary> |
309 | // { | 316 | /// <remarks> |
310 | // using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) | 317 | /// This gives us some insight into assets which haven't ben accessed for a long period. This is only done |
311 | // { | 318 | /// over the threshold time to avoid excessive database writes as assets are fetched. |
312 | // dbcon.Open(); | 319 | /// </remarks> |
313 | // MySqlCommand cmd = | 320 | /// <param name='asset'></param> |
314 | // new MySqlCommand("update assets set access_time=?access_time where id=?id", | 321 | /// <param name='accessTime'></param> |
315 | // dbcon); | 322 | private void UpdateAccessTime(AssetMetadata assetMetadata, int accessTime) |
316 | // | 323 | { |
317 | // // need to ensure we dispose | 324 | DateTime now = DateTime.UtcNow; |
318 | // try | 325 | |
319 | // { | 326 | if ((now - Utils.UnixTimeToDateTime(accessTime)).TotalDays < DaysBetweenAccessTimeUpdates) |
320 | // using (cmd) | 327 | return; |
321 | // { | 328 | |
322 | // // create unix epoch time | 329 | lock (m_dbLock) |
323 | // int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); | 330 | { |
324 | // cmd.Parameters.AddWithValue("?id", asset.ID); | 331 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) |
325 | // cmd.Parameters.AddWithValue("?access_time", now); | 332 | { |
326 | // cmd.ExecuteNonQuery(); | 333 | dbcon.Open(); |
327 | // cmd.Dispose(); | 334 | MySqlCommand cmd = |
328 | // } | 335 | new MySqlCommand("update XAssetsMeta set AccessTime=?AccessTime where ID=?ID", dbcon); |
329 | // } | 336 | |
330 | // catch (Exception e) | 337 | try |
331 | // { | 338 | { |
332 | // m_log.ErrorFormat( | 339 | using (cmd) |
333 | // "[ASSETS DB]: " + | 340 | { |
334 | // "MySql failure updating access_time for asset {0} with name {1}" + Environment.NewLine + e.ToString() | 341 | // create unix epoch time |
335 | // + Environment.NewLine + "Attempting reconnection", asset.FullID, asset.Name); | 342 | cmd.Parameters.AddWithValue("?ID", assetMetadata.ID); |
336 | // } | 343 | cmd.Parameters.AddWithValue("?AccessTime", (int)Utils.DateTimeToUnixTime(now)); |
337 | // } | 344 | cmd.ExecuteNonQuery(); |
338 | // } | 345 | } |
339 | // | 346 | } |
340 | // } | 347 | catch (Exception e) |
348 | { | ||
349 | m_log.ErrorFormat( | ||
350 | "[XASSET MYSQL DB]: Failure updating access_time for asset {0} with name {1}", | ||
351 | assetMetadata.ID, assetMetadata.Name); | ||
352 | } | ||
353 | } | ||
354 | } | ||
355 | } | ||
341 | 356 | ||
342 | /// <summary> | 357 | /// <summary> |
343 | /// We assume we already have the m_dbLock. | 358 | /// We assume we already have the m_dbLock. |
@@ -353,9 +368,9 @@ namespace OpenSim.Data.MySQL | |||
353 | 368 | ||
354 | bool exists = false; | 369 | bool exists = false; |
355 | 370 | ||
356 | using (MySqlCommand cmd = new MySqlCommand("SELECT hash FROM xassetsdata WHERE hash=?hash", dbcon)) | 371 | using (MySqlCommand cmd = new MySqlCommand("SELECT Hash FROM XAssetsData WHERE Hash=?Hash", dbcon)) |
357 | { | 372 | { |
358 | cmd.Parameters.AddWithValue("?hash", hash); | 373 | cmd.Parameters.AddWithValue("?Hash", hash); |
359 | 374 | ||
360 | try | 375 | try |
361 | { | 376 | { |
@@ -395,9 +410,9 @@ namespace OpenSim.Data.MySQL | |||
395 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) | 410 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) |
396 | { | 411 | { |
397 | dbcon.Open(); | 412 | dbcon.Open(); |
398 | using (MySqlCommand cmd = new MySqlCommand("SELECT id FROM xassetsmeta WHERE id=?id", dbcon)) | 413 | using (MySqlCommand cmd = new MySqlCommand("SELECT ID FROM XAssetsMeta WHERE ID=?ID", dbcon)) |
399 | { | 414 | { |
400 | cmd.Parameters.AddWithValue("?id", uuid.ToString()); | 415 | cmd.Parameters.AddWithValue("?ID", uuid.ToString()); |
401 | 416 | ||
402 | try | 417 | try |
403 | { | 418 | { |
@@ -412,8 +427,7 @@ namespace OpenSim.Data.MySQL | |||
412 | } | 427 | } |
413 | catch (Exception e) | 428 | catch (Exception e) |
414 | { | 429 | { |
415 | m_log.ErrorFormat( | 430 | m_log.Error(string.Format("[XASSETS DB]: MySql failure fetching asset {0}", uuid), e); |
416 | "[XASSETS DB]: MySql failure fetching asset {0}" + Environment.NewLine + e.ToString(), uuid); | ||
417 | } | 431 | } |
418 | } | 432 | } |
419 | } | 433 | } |
@@ -422,6 +436,7 @@ namespace OpenSim.Data.MySQL | |||
422 | return assetExists; | 436 | return assetExists; |
423 | } | 437 | } |
424 | 438 | ||
439 | |||
425 | /// <summary> | 440 | /// <summary> |
426 | /// Returns a list of AssetMetadata objects. The list is a subset of | 441 | /// Returns a list of AssetMetadata objects. The list is a subset of |
427 | /// the entire data set offset by <paramref name="start" /> containing | 442 | /// the entire data set offset by <paramref name="start" /> containing |
@@ -439,7 +454,7 @@ namespace OpenSim.Data.MySQL | |||
439 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) | 454 | using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) |
440 | { | 455 | { |
441 | dbcon.Open(); | 456 | dbcon.Open(); |
442 | MySqlCommand cmd = new MySqlCommand("SELECT name,description,asset_type,temporary,id,asset_flags,creator_id FROM xassetsmeta LIMIT ?start, ?count", dbcon); | 457 | MySqlCommand cmd = new MySqlCommand("SELECT Name, Description, AccessTime, AssetType, Temporary, ID, AssetFlags, CreatorID FROM XAssetsMeta LIMIT ?start, ?count", dbcon); |
443 | cmd.Parameters.AddWithValue("?start", start); | 458 | cmd.Parameters.AddWithValue("?start", start); |
444 | cmd.Parameters.AddWithValue("?count", count); | 459 | cmd.Parameters.AddWithValue("?count", count); |
445 | 460 | ||
@@ -450,17 +465,19 @@ namespace OpenSim.Data.MySQL | |||
450 | while (dbReader.Read()) | 465 | while (dbReader.Read()) |
451 | { | 466 | { |
452 | AssetMetadata metadata = new AssetMetadata(); | 467 | AssetMetadata metadata = new AssetMetadata(); |
453 | metadata.Name = (string)dbReader["name"]; | 468 | metadata.Name = (string)dbReader["Name"]; |
454 | metadata.Description = (string)dbReader["description"]; | 469 | metadata.Description = (string)dbReader["Description"]; |
455 | metadata.Type = (sbyte)dbReader["asset_type"]; | 470 | metadata.Type = (sbyte)dbReader["AssetType"]; |
456 | metadata.Temporary = Convert.ToBoolean(dbReader["temporary"]); // Not sure if this is correct. | 471 | metadata.Temporary = Convert.ToBoolean(dbReader["Temporary"]); // Not sure if this is correct. |
457 | metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); | 472 | metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["AssetFlags"]); |
458 | metadata.FullID = DBGuid.FromDB(dbReader["id"]); | 473 | metadata.FullID = DBGuid.FromDB(dbReader["ID"]); |
459 | metadata.CreatorID = dbReader["creator_id"].ToString(); | 474 | metadata.CreatorID = dbReader["CreatorID"].ToString(); |
460 | 475 | ||
461 | // We'll ignore this for now - it appears unused! | 476 | // We'll ignore this for now - it appears unused! |
462 | // metadata.SHA1 = dbReader["hash"]); | 477 | // metadata.SHA1 = dbReader["hash"]); |
463 | 478 | ||
479 | UpdateAccessTime(metadata, (int)dbReader["AccessTime"]); | ||
480 | |||
464 | retList.Add(metadata); | 481 | retList.Add(metadata); |
465 | } | 482 | } |
466 | } | 483 | } |
@@ -485,9 +502,9 @@ namespace OpenSim.Data.MySQL | |||
485 | { | 502 | { |
486 | dbcon.Open(); | 503 | dbcon.Open(); |
487 | 504 | ||
488 | using (MySqlCommand cmd = new MySqlCommand("delete from xassetsmeta where id=?id", dbcon)) | 505 | using (MySqlCommand cmd = new MySqlCommand("delete from XAssetsMeta where ID=?ID", dbcon)) |
489 | { | 506 | { |
490 | cmd.Parameters.AddWithValue("?id", id); | 507 | cmd.Parameters.AddWithValue("?ID", id); |
491 | cmd.ExecuteNonQuery(); | 508 | cmd.ExecuteNonQuery(); |
492 | } | 509 | } |
493 | 510 | ||
diff --git a/OpenSim/Data/MySQL/Resources/XAssetStore.migrations b/OpenSim/Data/MySQL/Resources/XAssetStore.migrations index d3cca5e..0c49d0d 100644 --- a/OpenSim/Data/MySQL/Resources/XAssetStore.migrations +++ b/OpenSim/Data/MySQL/Resources/XAssetStore.migrations | |||
@@ -3,24 +3,24 @@ | |||
3 | 3 | ||
4 | BEGIN; | 4 | BEGIN; |
5 | 5 | ||
6 | CREATE TABLE `xassetsmeta` ( | 6 | CREATE TABLE `XAssetsMeta` ( |
7 | `id` char(36) NOT NULL, | 7 | `ID` char(36) NOT NULL, |
8 | `hash` binary(32) NOT NULL, | 8 | `Hash` binary(32) NOT NULL, |
9 | `name` varchar(64) NOT NULL, | 9 | `Name` varchar(64) NOT NULL, |
10 | `description` varchar(64) NOT NULL, | 10 | `Description` varchar(64) NOT NULL, |
11 | `asset_type` tinyint(4) NOT NULL, | 11 | `AssetType` tinyint(4) NOT NULL, |
12 | `local` tinyint(1) NOT NULL, | 12 | `Local` tinyint(1) NOT NULL, |
13 | `temporary` tinyint(1) NOT NULL, | 13 | `Temporary` tinyint(1) NOT NULL, |
14 | `create_time` int(11) NOT NULL, | 14 | `CreateTime` int(11) NOT NULL, |
15 | `access_time` int(11) NOT NULL, | 15 | `AccessTime` int(11) NOT NULL, |
16 | `asset_flags` int(11) NOT NULL, | 16 | `AssetFlags` int(11) NOT NULL, |
17 | `creator_id` varchar(128) NOT NULL, | 17 | `CreatorID` varchar(128) NOT NULL, |
18 | PRIMARY KEY (`id`) | 18 | PRIMARY KEY (`id`) |
19 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; | 19 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; |
20 | 20 | ||
21 | CREATE TABLE `xassetsdata` ( | 21 | CREATE TABLE `XAssetsData` ( |
22 | `hash` binary(32) NOT NULL, | 22 | `Hash` binary(32) NOT NULL, |
23 | `data` longblob NOT NULL, | 23 | `Data` longblob NOT NULL, |
24 | PRIMARY KEY (`hash`) | 24 | PRIMARY KEY (`hash`) |
25 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; | 25 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; |
26 | 26 | ||
diff --git a/OpenSim/Framework/DAMap.cs b/OpenSim/Framework/DAMap.cs index 64cea77..df4a6bc 100644 --- a/OpenSim/Framework/DAMap.cs +++ b/OpenSim/Framework/DAMap.cs | |||
@@ -180,7 +180,7 @@ namespace OpenSim.Framework | |||
180 | /// Validate the key used for storing separate data stores. | 180 | /// Validate the key used for storing separate data stores. |
181 | /// </summary> | 181 | /// </summary> |
182 | /// <param name='key'></param> | 182 | /// <param name='key'></param> |
183 | private static void ValidateKey(string key) | 183 | public static void ValidateKey(string key) |
184 | { | 184 | { |
185 | if (key.Length < MIN_STORE_NAME_LENGTH) | 185 | if (key.Length < MIN_STORE_NAME_LENGTH) |
186 | throw new Exception("Minimum store name length is " + MIN_STORE_NAME_LENGTH); | 186 | throw new Exception("Minimum store name length is " + MIN_STORE_NAME_LENGTH); |
diff --git a/OpenSim/ApplicationPlugins/Rest/RestXmlWriter.cs b/OpenSim/Framework/DOMap.cs index 283fa2e..755e129 100644 --- a/OpenSim/ApplicationPlugins/Rest/RestXmlWriter.cs +++ b/OpenSim/Framework/DOMap.cs | |||
@@ -25,48 +25,74 @@ | |||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
28 | using System.IO; | 31 | using System.IO; |
29 | using System.Text; | 32 | using System.Text; |
30 | using System.Xml; | 33 | using System.Xml; |
34 | using System.Xml.Schema; | ||
35 | using System.Xml.Serialization; | ||
36 | using OpenMetaverse; | ||
37 | using OpenMetaverse.StructuredData; | ||
31 | 38 | ||
32 | namespace OpenSim.ApplicationPlugins.Rest | 39 | namespace OpenSim.Framework |
33 | { | 40 | { |
34 | public class RestXmlWriter: XmlTextWriter | 41 | /// <summary> |
42 | /// This class stores and retrieves dynamic objects. | ||
43 | /// </summary> | ||
44 | /// <remarks> | ||
45 | /// Experimental - DO NOT USE. | ||
46 | /// </remarks> | ||
47 | public class DOMap | ||
35 | { | 48 | { |
36 | private StringWriter m_sw = null; | 49 | private IDictionary<string, object> m_map; |
37 | 50 | ||
38 | public RestXmlWriter(StringWriter sw) : base(sw) | 51 | public void Add(string key, object dynObj) |
39 | { | ||
40 | m_sw = sw; | ||
41 | Formatting = Formatting.Indented; | ||
42 | } | ||
43 | |||
44 | public RestXmlWriter(TextWriter textWriter) : base(textWriter) | ||
45 | { | 52 | { |
46 | } | 53 | DAMap.ValidateKey(key); |
47 | 54 | ||
48 | public RestXmlWriter(Stream stream) | 55 | lock (this) |
49 | : this(stream, Encoding.UTF8) | 56 | { |
50 | { | 57 | if (m_map == null) |
51 | } | 58 | m_map = new Dictionary<string, object>(); |
52 | 59 | ||
53 | public RestXmlWriter(Stream stream, Encoding enc) : base(stream, enc) | 60 | m_map.Add(key, dynObj); |
54 | { | 61 | } |
55 | } | 62 | } |
56 | 63 | ||
57 | public override void WriteStartDocument() | 64 | public bool ContainsKey(string key) |
58 | { | 65 | { |
66 | return Get(key) != null; | ||
59 | } | 67 | } |
60 | 68 | ||
61 | public override void WriteStartDocument(bool standalone) | 69 | /// <summary> |
70 | /// Get a dynamic object | ||
71 | /// </summary> | ||
72 | /// <remarks> | ||
73 | /// Not providing an index method so that users can't casually overwrite each other's objects. | ||
74 | /// </remarks> | ||
75 | /// <param name='key'></param> | ||
76 | public object Get(string key) | ||
62 | { | 77 | { |
78 | lock (this) | ||
79 | { | ||
80 | if (m_map == null) | ||
81 | return null; | ||
82 | else | ||
83 | return m_map[key]; | ||
84 | } | ||
63 | } | 85 | } |
64 | 86 | ||
65 | public override string ToString() | 87 | public bool Remove(string key) |
66 | { | 88 | { |
67 | Flush(); | 89 | lock (this) |
68 | Close(); | 90 | { |
69 | return m_sw.ToString(); | 91 | if (m_map == null) |
92 | return false; | ||
93 | else | ||
94 | return m_map.Remove(key); | ||
95 | } | ||
70 | } | 96 | } |
71 | } | 97 | } |
72 | } | 98 | } \ No newline at end of file |
diff --git a/OpenSim/Framework/ILandChannel.cs b/OpenSim/Framework/ILandChannel.cs index 869d4c8..c46c03c 100644 --- a/OpenSim/Framework/ILandChannel.cs +++ b/OpenSim/Framework/ILandChannel.cs | |||
@@ -56,6 +56,13 @@ namespace OpenSim.Region.Framework.Interfaces | |||
56 | ILandObject GetLandObject(float x, float y); | 56 | ILandObject GetLandObject(float x, float y); |
57 | 57 | ||
58 | /// <summary> | 58 | /// <summary> |
59 | /// Get the parcel at the specified point | ||
60 | /// </summary> | ||
61 | /// <param name="position">Vector where x and y components are between 0 and 256. z component is ignored.</param> | ||
62 | /// <returns>Land object at the point supplied</returns> | ||
63 | ILandObject GetLandObject(Vector3 position); | ||
64 | |||
65 | /// <summary> | ||
59 | /// Get the parcels near the specified point | 66 | /// Get the parcels near the specified point |
60 | /// </summary> | 67 | /// </summary> |
61 | /// <param name="position"></param> | 68 | /// <param name="position"></param> |
diff --git a/OpenSim/Framework/PluginManager.cs b/OpenSim/Framework/PluginManager.cs index 00263f5..0117096 100644 --- a/OpenSim/Framework/PluginManager.cs +++ b/OpenSim/Framework/PluginManager.cs | |||
@@ -218,7 +218,7 @@ namespace OpenSim.Framework | |||
218 | Console.WriteLine ("Looking for updates..."); | 218 | Console.WriteLine ("Looking for updates..."); |
219 | Repositories.UpdateAllRepositories (ps); | 219 | Repositories.UpdateAllRepositories (ps); |
220 | Console.WriteLine ("Available add-in updates:"); | 220 | Console.WriteLine ("Available add-in updates:"); |
221 | bool found = false; | 221 | |
222 | AddinRepositoryEntry[] entries = Repositories.GetAvailableUpdates(); | 222 | AddinRepositoryEntry[] entries = Repositories.GetAvailableUpdates(); |
223 | 223 | ||
224 | foreach (AddinRepositoryEntry entry in entries) | 224 | foreach (AddinRepositoryEntry entry in entries) |
@@ -541,7 +541,7 @@ namespace OpenSim.Framework | |||
541 | { | 541 | { |
542 | list.AddRange(PluginRegistry.GetAddins()); | 542 | list.AddRange(PluginRegistry.GetAddins()); |
543 | } | 543 | } |
544 | catch(Exception e) | 544 | catch (Exception) |
545 | { | 545 | { |
546 | Addin[] x = xlist.ToArray(typeof(Addin)) as Addin[]; | 546 | Addin[] x = xlist.ToArray(typeof(Addin)) as Addin[]; |
547 | return x; | 547 | return x; |
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index c0dc907..035b3ad 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs | |||
@@ -133,17 +133,7 @@ namespace OpenSim.Framework.Servers | |||
133 | /// Performs initialisation of the scene, such as loading configuration from disk. | 133 | /// Performs initialisation of the scene, such as loading configuration from disk. |
134 | /// </summary> | 134 | /// </summary> |
135 | public virtual void Startup() | 135 | public virtual void Startup() |
136 | { | 136 | { |
137 | m_log.Info("[STARTUP]: Beginning startup processing"); | ||
138 | |||
139 | m_log.Info("[STARTUP]: OpenSimulator version: " + m_version + Environment.NewLine); | ||
140 | // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and | ||
141 | // the clr version number doesn't match the project version number under Mono. | ||
142 | //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine); | ||
143 | m_log.InfoFormat( | ||
144 | "[STARTUP]: Operating system version: {0}, .NET platform {1}, {2}-bit\n", | ||
145 | Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32"); | ||
146 | |||
147 | StartupSpecific(); | 137 | StartupSpecific(); |
148 | 138 | ||
149 | TimeSpan timeTaken = DateTime.Now - m_startuptime; | 139 | TimeSpan timeTaken = DateTime.Now - m_startuptime; |
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 70c531c..dfdd566 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs | |||
@@ -486,7 +486,9 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
486 | { | 486 | { |
487 | try | 487 | try |
488 | { | 488 | { |
489 | SendHTML500(response); | 489 | byte[] buffer500 = SendHTML500(response); |
490 | response.Body.Write(buffer500,0,buffer500.Length); | ||
491 | response.Body.Close(); | ||
490 | } | 492 | } |
491 | catch | 493 | catch |
492 | { | 494 | { |
@@ -719,7 +721,15 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
719 | catch (Exception e) | 721 | catch (Exception e) |
720 | { | 722 | { |
721 | m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); | 723 | m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); |
722 | SendHTML500(response); | 724 | try |
725 | { | ||
726 | byte[] buffer500 = SendHTML500(response); | ||
727 | response.Body.Write(buffer500, 0, buffer500.Length); | ||
728 | response.Body.Close(); | ||
729 | } | ||
730 | catch | ||
731 | { | ||
732 | } | ||
723 | } | 733 | } |
724 | finally | 734 | finally |
725 | { | 735 | { |
@@ -1746,7 +1756,8 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
1746 | response.SendChunked = false; | 1756 | response.SendChunked = false; |
1747 | response.ContentLength64 = buffer.Length; | 1757 | response.ContentLength64 = buffer.Length; |
1748 | response.ContentEncoding = Encoding.UTF8; | 1758 | response.ContentEncoding = Encoding.UTF8; |
1749 | 1759 | ||
1760 | |||
1750 | return buffer; | 1761 | return buffer; |
1751 | } | 1762 | } |
1752 | 1763 | ||
@@ -1912,6 +1923,12 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
1912 | m_rpcHandlers.Remove(method); | 1923 | m_rpcHandlers.Remove(method); |
1913 | } | 1924 | } |
1914 | 1925 | ||
1926 | public void RemoveJsonRPCHandler(string method) | ||
1927 | { | ||
1928 | lock(jsonRpcHandlers) | ||
1929 | jsonRpcHandlers.Remove(method); | ||
1930 | } | ||
1931 | |||
1915 | public bool RemoveLLSDHandler(string path, LLSDMethod handler) | 1932 | public bool RemoveLLSDHandler(string path, LLSDMethod handler) |
1916 | { | 1933 | { |
1917 | lock (m_llsdHandlers) | 1934 | lock (m_llsdHandlers) |
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs index 71ca3ff..d162bc1 100644 --- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs | |||
@@ -140,6 +140,8 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
140 | void RemoveStreamHandler(string httpMethod, string path); | 140 | void RemoveStreamHandler(string httpMethod, string path); |
141 | 141 | ||
142 | void RemoveXmlRPCHandler(string method); | 142 | void RemoveXmlRPCHandler(string method); |
143 | |||
144 | void RemoveJsonRPCHandler(string method); | ||
143 | 145 | ||
144 | string GetHTTP404(string host); | 146 | string GetHTTP404(string host); |
145 | 147 | ||
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs index 47baac8..657444c 100644 --- a/OpenSim/Framework/Servers/ServerBase.cs +++ b/OpenSim/Framework/Servers/ServerBase.cs | |||
@@ -113,6 +113,26 @@ namespace OpenSim.Framework.Servers | |||
113 | } | 113 | } |
114 | } | 114 | } |
115 | 115 | ||
116 | /// <summary> | ||
117 | /// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details, | ||
118 | /// etc.). | ||
119 | /// </summary> | ||
120 | public void LogEnvironmentInformation() | ||
121 | { | ||
122 | // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net | ||
123 | // XmlConfigurator calls first accross servers. | ||
124 | m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory); | ||
125 | |||
126 | m_log.InfoFormat("[SERVER BASE]: OpenSimulator version: {0}", m_version); | ||
127 | |||
128 | // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and | ||
129 | // the clr version number doesn't match the project version number under Mono. | ||
130 | //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine); | ||
131 | m_log.InfoFormat( | ||
132 | "[SERVER BASE]: Operating system version: {0}, .NET platform {1}, {2}-bit", | ||
133 | Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32"); | ||
134 | } | ||
135 | |||
116 | public void RegisterCommonAppenders(IConfig startupConfig) | 136 | public void RegisterCommonAppenders(IConfig startupConfig) |
117 | { | 137 | { |
118 | ILoggerRepository repository = LogManager.GetRepository(); | 138 | ILoggerRepository repository = LogManager.GetRepository(); |
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 0fa54b2..94a172c 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs | |||
@@ -303,12 +303,12 @@ namespace OpenSim.Framework | |||
303 | // Clamp the maximum magnitude of a vector | 303 | // Clamp the maximum magnitude of a vector |
304 | public static Vector3 ClampV(Vector3 x, float max) | 304 | public static Vector3 ClampV(Vector3 x, float max) |
305 | { | 305 | { |
306 | Vector3 ret = x; | ||
307 | float lenSq = x.LengthSquared(); | 306 | float lenSq = x.LengthSquared(); |
308 | if (lenSq > (max * max)) | 307 | if (lenSq > (max * max)) |
309 | { | 308 | { |
310 | x = x / x.Length() * max; | 309 | x = x / x.Length() * max; |
311 | } | 310 | } |
311 | |||
312 | return x; | 312 | return x; |
313 | } | 313 | } |
314 | 314 | ||
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 4075edb..11dd052 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs | |||
@@ -159,6 +159,7 @@ namespace OpenSim | |||
159 | 159 | ||
160 | MainConsole.Instance = m_console; | 160 | MainConsole.Instance = m_console; |
161 | 161 | ||
162 | LogEnvironmentInformation(); | ||
162 | RegisterCommonAppenders(Config.Configs["Startup"]); | 163 | RegisterCommonAppenders(Config.Configs["Startup"]); |
163 | RegisterConsoleCommands(); | 164 | RegisterConsoleCommands(); |
164 | 165 | ||
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index 137bd81..c555915 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs | |||
@@ -134,10 +134,6 @@ namespace OpenSim | |||
134 | /// <param name="configSource"></param> | 134 | /// <param name="configSource"></param> |
135 | public OpenSimBase(IConfigSource configSource) : base() | 135 | public OpenSimBase(IConfigSource configSource) : base() |
136 | { | 136 | { |
137 | // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net | ||
138 | // XmlConfigurator calls first accross servers. | ||
139 | m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory); | ||
140 | |||
141 | LoadConfigSettings(configSource); | 137 | LoadConfigSettings(configSource); |
142 | } | 138 | } |
143 | 139 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs index ed8ec16..141af8a 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs | |||
@@ -49,8 +49,10 @@ namespace OpenSim.Region.ClientStack.Linden.Tests | |||
49 | private TestScene m_scene; | 49 | private TestScene m_scene; |
50 | 50 | ||
51 | [SetUp] | 51 | [SetUp] |
52 | public void SetUp() | 52 | public override void SetUp() |
53 | { | 53 | { |
54 | base.SetUp(); | ||
55 | |||
54 | uint port = 9999; | 56 | uint port = 9999; |
55 | uint sslPort = 9998; | 57 | uint sslPort = 9998; |
56 | 58 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 6742d99..7ea538c 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | |||
@@ -4581,7 +4581,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4581 | rinfopack.AgentData = new RegionInfoPacket.AgentDataBlock(); | 4581 | rinfopack.AgentData = new RegionInfoPacket.AgentDataBlock(); |
4582 | rinfopack.AgentData.AgentID = AgentId; | 4582 | rinfopack.AgentData.AgentID = AgentId; |
4583 | rinfopack.AgentData.SessionID = SessionId; | 4583 | rinfopack.AgentData.SessionID = SessionId; |
4584 | 4584 | rinfopack.RegionInfo3 = new RegionInfoPacket.RegionInfo3Block[0]; | |
4585 | 4585 | ||
4586 | OutPacket(rinfopack, ThrottleOutPacketType.Task); | 4586 | OutPacket(rinfopack, ThrottleOutPacketType.Task); |
4587 | } | 4587 | } |
@@ -7069,7 +7069,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
7069 | 7069 | ||
7070 | if (handlerUpdatePrimFlags != null) | 7070 | if (handlerUpdatePrimFlags != null) |
7071 | { | 7071 | { |
7072 | byte[] data = Pack.ToBytes(); | 7072 | // byte[] data = Pack.ToBytes(); |
7073 | // 46,47,48 are special positions within the packet | 7073 | // 46,47,48 are special positions within the packet |
7074 | // This may change so perhaps we need a better way | 7074 | // This may change so perhaps we need a better way |
7075 | // of storing this (OMV.FlagUpdatePacket.UsePhysics,etc?) | 7075 | // of storing this (OMV.FlagUpdatePacket.UsePhysics,etc?) |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index a7628d2..72516cd 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | |||
@@ -278,25 +278,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
278 | m_shouldCollectStats = false; | 278 | m_shouldCollectStats = false; |
279 | if (config != null) | 279 | if (config != null) |
280 | { | 280 | { |
281 | if (config.Contains("enabled") && config.GetBoolean("enabled")) | 281 | m_shouldCollectStats = config.GetBoolean("Enabled", false); |
282 | { | 282 | binStatsMaxFilesize = TimeSpan.FromSeconds(config.GetInt("packet_headers_period_seconds", 300)); |
283 | if (config.Contains("collect_packet_headers")) | 283 | binStatsDir = config.GetString("stats_dir", "."); |
284 | m_shouldCollectStats = config.GetBoolean("collect_packet_headers"); | 284 | m_aggregatedBWStats = config.GetBoolean("aggregatedBWStats", false); |
285 | if (config.Contains("packet_headers_period_seconds")) | 285 | } |
286 | { | 286 | #endregion BinaryStats |
287 | binStatsMaxFilesize = TimeSpan.FromSeconds(config.GetInt("region_stats_period_seconds")); | ||
288 | } | ||
289 | if (config.Contains("stats_dir")) | ||
290 | { | ||
291 | binStatsDir = config.GetString("stats_dir"); | ||
292 | } | ||
293 | } | ||
294 | else | ||
295 | { | ||
296 | m_shouldCollectStats = false; | ||
297 | } | ||
298 | } | ||
299 | #endregion BinaryStats | ||
300 | 287 | ||
301 | m_throttle = new TokenBucket(null, sceneThrottleBps); | 288 | m_throttle = new TokenBucket(null, sceneThrottleBps); |
302 | ThrottleRates = new ThrottleRates(configSource); | 289 | ThrottleRates = new ThrottleRates(configSource); |
@@ -1266,8 +1253,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1266 | static object binStatsLogLock = new object(); | 1253 | static object binStatsLogLock = new object(); |
1267 | static string binStatsDir = ""; | 1254 | static string binStatsDir = ""; |
1268 | 1255 | ||
1256 | //for Aggregated In/Out BW logging | ||
1257 | static bool m_aggregatedBWStats = false; | ||
1258 | static long m_aggregatedBytesIn = 0; | ||
1259 | static long m_aggregatedByestOut = 0; | ||
1260 | static object aggBWStatsLock = new object(); | ||
1261 | |||
1262 | public static long AggregatedLLUDPBytesIn | ||
1263 | { | ||
1264 | get { return m_aggregatedBytesIn; } | ||
1265 | } | ||
1266 | public static long AggregatedLLUDPBytesOut | ||
1267 | { | ||
1268 | get {return m_aggregatedByestOut;} | ||
1269 | } | ||
1270 | |||
1269 | public static void LogPacketHeader(bool incoming, uint circuit, byte flags, PacketType packetType, ushort size) | 1271 | public static void LogPacketHeader(bool incoming, uint circuit, byte flags, PacketType packetType, ushort size) |
1270 | { | 1272 | { |
1273 | if (m_aggregatedBWStats) | ||
1274 | { | ||
1275 | lock (aggBWStatsLock) | ||
1276 | { | ||
1277 | if (incoming) | ||
1278 | m_aggregatedBytesIn += size; | ||
1279 | else | ||
1280 | m_aggregatedByestOut += size; | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1271 | if (!m_shouldCollectStats) return; | 1284 | if (!m_shouldCollectStats) return; |
1272 | 1285 | ||
1273 | // Binary logging format is TTTTTTTTCCCCFPPPSS, T=Time, C=Circuit, F=Flags, P=PacketType, S=size | 1286 | // Binary logging format is TTTTTTTTCCCCFPPPSS, T=Time, C=Circuit, F=Flags, P=PacketType, S=size |
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index ab7e932..2dea14d 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs | |||
@@ -289,21 +289,22 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
289 | if (!Enabled) | 289 | if (!Enabled) |
290 | return false; | 290 | return false; |
291 | 291 | ||
292 | if (AttachObjectInternal(sp, group, attachmentPt, silent, temp, append)) | 292 | return AttachObjectInternal(sp, group, attachmentPt, silent, temp, append); |
293 | { | ||
294 | m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID); | ||
295 | return true; | ||
296 | } | ||
297 | |||
298 | return false; | ||
299 | } | 293 | } |
300 | |||
301 | private bool AttachObjectInternal(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool temp, bool append) | ||
302 | { | ||
303 | // m_log.DebugFormat( | ||
304 | // "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})", | ||
305 | // group.Name, group.LocalId, sp.Name, attachmentPt, silent); | ||
306 | 294 | ||
295 | /// <summary> | ||
296 | /// Internal method which actually does all the work for attaching an object. | ||
297 | /// </summary> | ||
298 | /// <returns>The object attached.</returns> | ||
299 | /// <param name='sp'></param> | ||
300 | /// <param name='group'>The object to attach.</param> | ||
301 | /// <param name='attachmentPt'></param> | ||
302 | /// <param name='silent'></param> | ||
303 | /// <param name='temp'></param> | ||
304 | /// <param name='resumeScripts'>If true then scripts are resumed on the attached object.</param> | ||
305 | private bool AttachObjectInternal( | ||
306 | IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool temp, bool resumeScripts) | ||
307 | { | ||
307 | if (group.GetSittingAvatarsCount() != 0) | 308 | if (group.GetSittingAvatarsCount() != 0) |
308 | { | 309 | { |
309 | // m_log.WarnFormat( | 310 | // m_log.WarnFormat( |
@@ -314,6 +315,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
314 | } | 315 | } |
315 | 316 | ||
316 | List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt); | 317 | List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt); |
318 | |||
317 | if (attachments.Contains(group)) | 319 | if (attachments.Contains(group)) |
318 | { | 320 | { |
319 | // m_log.WarnFormat( | 321 | // m_log.WarnFormat( |
@@ -374,6 +376,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
374 | UpdateUserInventoryWithAttachment(sp, group, attachmentPt, temp, append); | 376 | UpdateUserInventoryWithAttachment(sp, group, attachmentPt, temp, append); |
375 | 377 | ||
376 | AttachToAgent(sp, group, attachmentPt, attachPos, silent); | 378 | AttachToAgent(sp, group, attachmentPt, attachPos, silent); |
379 | |||
380 | if (resumeScripts) | ||
381 | { | ||
382 | // Fire after attach, so we don't get messy perms dialogs | ||
383 | // 4 == AttachedRez | ||
384 | group.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4); | ||
385 | group.ResumeScripts(); | ||
386 | } | ||
387 | |||
388 | // Do this last so that event listeners have access to all the effects of the attachment | ||
389 | m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID); | ||
377 | } | 390 | } |
378 | 391 | ||
379 | return true; | 392 | return true; |
@@ -400,8 +413,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
400 | return null; | 413 | return null; |
401 | 414 | ||
402 | // m_log.DebugFormat( | 415 | // m_log.DebugFormat( |
403 | // "[ATTACHMENTS MODULE]: RezSingleAttachmentFromInventory to point {0} from item {1} for {2}", | 416 | // "[ATTACHMENTS MODULE]: RezSingleAttachmentFromInventory to point {0} from item {1} for {2} in {3}", |
404 | // (AttachmentPoint)AttachmentPt, itemID, sp.Name); | 417 | // (AttachmentPoint)AttachmentPt, itemID, sp.Name, m_scene.Name); |
405 | 418 | ||
406 | bool append = (AttachmentPt & 0x80) != 0; | 419 | bool append = (AttachmentPt & 0x80) != 0; |
407 | AttachmentPt &= 0x7f; | 420 | AttachmentPt &= 0x7f; |
@@ -533,6 +546,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
533 | return; | 546 | return; |
534 | } | 547 | } |
535 | 548 | ||
549 | // m_log.DebugFormat( | ||
550 | // "[ATTACHMENTS MODULE]: Detaching object {0} {1} for {2} in {3}", | ||
551 | // so.Name, so.LocalId, sp.Name, m_scene.Name); | ||
552 | |||
536 | // Scripts MUST be snapshotted before the object is | 553 | // Scripts MUST be snapshotted before the object is |
537 | // removed from the scene because doing otherwise will | 554 | // removed from the scene because doing otherwise will |
538 | // clobber the run flag | 555 | // clobber the run flag |
@@ -854,61 +871,42 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
854 | return null; | 871 | return null; |
855 | } | 872 | } |
856 | 873 | ||
857 | // Remove any previous attachments | ||
858 | List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt); | ||
859 | string previousAttachmentScriptedState = null; | ||
860 | |||
861 | // At the moment we can only deal with a single attachment | ||
862 | if (attachments.Count != 0) | ||
863 | DetachSingleAttachmentToInv(sp, attachments[0]); | ||
864 | |||
865 | lock (sp.AttachmentsSyncLock) | ||
866 | { | ||
867 | // m_log.DebugFormat( | 874 | // m_log.DebugFormat( |
868 | // "[ATTACHMENTS MODULE]: Rezzed single object {0} for attachment to {1} on point {2} in {3}", | 875 | // "[ATTACHMENTS MODULE]: Rezzed single object {0} for attachment to {1} on point {2} in {3}", |
869 | // objatt.Name, sp.Name, attachmentPt, m_scene.Name); | 876 | // objatt.Name, sp.Name, attachmentPt, m_scene.Name); |
870 | 877 | ||
871 | // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller. | 878 | // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller. |
872 | objatt.HasGroupChanged = false; | 879 | objatt.HasGroupChanged = false; |
873 | bool tainted = false; | 880 | bool tainted = false; |
874 | if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint) | 881 | if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint) |
875 | tainted = true; | 882 | tainted = true; |
876 | 883 | ||
877 | // FIXME: Detect whether it's really likely for AttachObject to throw an exception in the normal | 884 | // FIXME: Detect whether it's really likely for AttachObject to throw an exception in the normal |
878 | // course of events. If not, then it's probably not worth trying to recover the situation | 885 | // course of events. If not, then it's probably not worth trying to recover the situation |
879 | // since this is more likely to trigger further exceptions and confuse later debugging. If | 886 | // since this is more likely to trigger further exceptions and confuse later debugging. If |
880 | // exceptions can be thrown in expected error conditions (not NREs) then make this consistent | 887 | // exceptions can be thrown in expected error conditions (not NREs) then make this consistent |
881 | // since other normal error conditions will simply return false instead. | 888 | // since other normal error conditions will simply return false instead. |
882 | // This will throw if the attachment fails | 889 | // This will throw if the attachment fails |
883 | try | 890 | try |
884 | { | 891 | { |
885 | AttachObjectInternal(sp, objatt, attachmentPt, false, false, append); | 892 | AttachObjectInternal(sp, objatt, attachmentPt, false, false, append); |
886 | } | 893 | } |
887 | catch (Exception e) | 894 | catch (Exception e) |
888 | { | 895 | { |
889 | m_log.ErrorFormat( | 896 | m_log.ErrorFormat( |
890 | "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}", | 897 | "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}", |
891 | objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace); | 898 | objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace); |
892 | |||
893 | // Make sure the object doesn't stick around and bail | ||
894 | sp.RemoveAttachment(objatt); | ||
895 | m_scene.DeleteSceneObject(objatt, false); | ||
896 | return null; | ||
897 | } | ||
898 | |||
899 | if (tainted) | ||
900 | objatt.HasGroupChanged = true; | ||
901 | 899 | ||
902 | // Fire after attach, so we don't get messy perms dialogs | 900 | // Make sure the object doesn't stick around and bail |
903 | // 4 == AttachedRez | 901 | sp.RemoveAttachment(objatt); |
904 | objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4); | 902 | m_scene.DeleteSceneObject(objatt, false); |
905 | objatt.ResumeScripts(); | 903 | return null; |
904 | } | ||
906 | 905 | ||
907 | // Do this last so that event listeners have access to all the effects of the attachment | 906 | if (tainted) |
908 | m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, sp.UUID); | 907 | objatt.HasGroupChanged = true; |
909 | 908 | ||
910 | return objatt; | 909 | return objatt; |
911 | } | ||
912 | } | 910 | } |
913 | 911 | ||
914 | /// <summary> | 912 | /// <summary> |
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs index f48bb6f..0c1df6a 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs | |||
@@ -228,6 +228,120 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests | |||
228 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); | 228 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); |
229 | } | 229 | } |
230 | 230 | ||
231 | [Test] | ||
232 | public void TestWearAttachmentFromGround() | ||
233 | { | ||
234 | TestHelpers.InMethod(); | ||
235 | // TestHelpers.EnableLogging(); | ||
236 | |||
237 | Scene scene = CreateTestScene(); | ||
238 | UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1); | ||
239 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1); | ||
240 | |||
241 | SceneObjectGroup so2 = SceneHelpers.AddSceneObject(scene, "att2", sp.UUID); | ||
242 | |||
243 | { | ||
244 | SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, "att1", sp.UUID); | ||
245 | |||
246 | m_numberOfAttachEventsFired = 0; | ||
247 | scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Default, false, false); | ||
248 | |||
249 | // Check status on scene presence | ||
250 | Assert.That(sp.HasAttachments(), Is.True); | ||
251 | List<SceneObjectGroup> attachments = sp.GetAttachments(); | ||
252 | Assert.That(attachments.Count, Is.EqualTo(1)); | ||
253 | SceneObjectGroup attSo = attachments[0]; | ||
254 | Assert.That(attSo.Name, Is.EqualTo(so.Name)); | ||
255 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand)); | ||
256 | Assert.That(attSo.IsAttachment); | ||
257 | Assert.That(attSo.UsesPhysics, Is.False); | ||
258 | Assert.That(attSo.IsTemporary, Is.False); | ||
259 | |||
260 | // Check item status | ||
261 | Assert.That( | ||
262 | sp.Appearance.GetAttachpoint(attSo.FromItemID), | ||
263 | Is.EqualTo((int)AttachmentPoint.LeftHand)); | ||
264 | |||
265 | InventoryItemBase attachmentItem = scene.InventoryService.GetItem(new InventoryItemBase(attSo.FromItemID)); | ||
266 | Assert.That(attachmentItem, Is.Not.Null); | ||
267 | Assert.That(attachmentItem.Name, Is.EqualTo(so.Name)); | ||
268 | |||
269 | InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, AssetType.Object); | ||
270 | Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID)); | ||
271 | |||
272 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(2)); | ||
273 | |||
274 | // Check events | ||
275 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); | ||
276 | } | ||
277 | |||
278 | // Test wearing a different attachment from the ground. | ||
279 | { | ||
280 | scene.AttachmentsModule.AttachObject(sp, so2, (uint)AttachmentPoint.Default, false, false); | ||
281 | |||
282 | // Check status on scene presence | ||
283 | Assert.That(sp.HasAttachments(), Is.True); | ||
284 | List<SceneObjectGroup> attachments = sp.GetAttachments(); | ||
285 | Assert.That(attachments.Count, Is.EqualTo(1)); | ||
286 | SceneObjectGroup attSo = attachments[0]; | ||
287 | Assert.That(attSo.Name, Is.EqualTo(so2.Name)); | ||
288 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand)); | ||
289 | Assert.That(attSo.IsAttachment); | ||
290 | Assert.That(attSo.UsesPhysics, Is.False); | ||
291 | Assert.That(attSo.IsTemporary, Is.False); | ||
292 | |||
293 | // Check item status | ||
294 | Assert.That( | ||
295 | sp.Appearance.GetAttachpoint(attSo.FromItemID), | ||
296 | Is.EqualTo((int)AttachmentPoint.LeftHand)); | ||
297 | |||
298 | InventoryItemBase attachmentItem = scene.InventoryService.GetItem(new InventoryItemBase(attSo.FromItemID)); | ||
299 | Assert.That(attachmentItem, Is.Not.Null); | ||
300 | Assert.That(attachmentItem.Name, Is.EqualTo(so2.Name)); | ||
301 | |||
302 | InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, AssetType.Object); | ||
303 | Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID)); | ||
304 | |||
305 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | ||
306 | |||
307 | // Check events | ||
308 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3)); | ||
309 | } | ||
310 | |||
311 | // Test rewearing an already worn attachment from ground. Nothing should happen. | ||
312 | { | ||
313 | scene.AttachmentsModule.AttachObject(sp, so2, (uint)AttachmentPoint.Default, false, false); | ||
314 | |||
315 | // Check status on scene presence | ||
316 | Assert.That(sp.HasAttachments(), Is.True); | ||
317 | List<SceneObjectGroup> attachments = sp.GetAttachments(); | ||
318 | Assert.That(attachments.Count, Is.EqualTo(1)); | ||
319 | SceneObjectGroup attSo = attachments[0]; | ||
320 | Assert.That(attSo.Name, Is.EqualTo(so2.Name)); | ||
321 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand)); | ||
322 | Assert.That(attSo.IsAttachment); | ||
323 | Assert.That(attSo.UsesPhysics, Is.False); | ||
324 | Assert.That(attSo.IsTemporary, Is.False); | ||
325 | |||
326 | // Check item status | ||
327 | Assert.That( | ||
328 | sp.Appearance.GetAttachpoint(attSo.FromItemID), | ||
329 | Is.EqualTo((int)AttachmentPoint.LeftHand)); | ||
330 | |||
331 | InventoryItemBase attachmentItem = scene.InventoryService.GetItem(new InventoryItemBase(attSo.FromItemID)); | ||
332 | Assert.That(attachmentItem, Is.Not.Null); | ||
333 | Assert.That(attachmentItem.Name, Is.EqualTo(so2.Name)); | ||
334 | |||
335 | InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, AssetType.Object); | ||
336 | Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID)); | ||
337 | |||
338 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | ||
339 | |||
340 | // Check events | ||
341 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3)); | ||
342 | } | ||
343 | } | ||
344 | |||
231 | /// <summary> | 345 | /// <summary> |
232 | /// Test that we do not attempt to attach an in-world object that someone else is sitting on. | 346 | /// Test that we do not attempt to attach an in-world object that someone else is sitting on. |
233 | /// </summary> | 347 | /// </summary> |
@@ -275,29 +389,140 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests | |||
275 | 389 | ||
276 | InventoryItemBase attItem = CreateAttachmentItem(scene, ua1.PrincipalID, "att", 0x10, 0x20); | 390 | InventoryItemBase attItem = CreateAttachmentItem(scene, ua1.PrincipalID, "att", 0x10, 0x20); |
277 | 391 | ||
278 | m_numberOfAttachEventsFired = 0; | 392 | { |
279 | scene.AttachmentsModule.RezSingleAttachmentFromInventory( | 393 | scene.AttachmentsModule.RezSingleAttachmentFromInventory( |
280 | sp, attItem.ID, (uint)AttachmentPoint.Chest); | 394 | sp, attItem.ID, (uint)AttachmentPoint.Chest); |
281 | 395 | ||
282 | // Check scene presence status | 396 | // Check scene presence status |
283 | Assert.That(sp.HasAttachments(), Is.True); | 397 | Assert.That(sp.HasAttachments(), Is.True); |
284 | List<SceneObjectGroup> attachments = sp.GetAttachments(); | 398 | List<SceneObjectGroup> attachments = sp.GetAttachments(); |
285 | Assert.That(attachments.Count, Is.EqualTo(1)); | 399 | Assert.That(attachments.Count, Is.EqualTo(1)); |
286 | SceneObjectGroup attSo = attachments[0]; | 400 | SceneObjectGroup attSo = attachments[0]; |
287 | Assert.That(attSo.Name, Is.EqualTo(attItem.Name)); | 401 | Assert.That(attSo.Name, Is.EqualTo(attItem.Name)); |
288 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.Chest)); | 402 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.Chest)); |
289 | Assert.That(attSo.IsAttachment); | 403 | Assert.That(attSo.IsAttachment); |
290 | Assert.That(attSo.UsesPhysics, Is.False); | 404 | Assert.That(attSo.UsesPhysics, Is.False); |
291 | Assert.That(attSo.IsTemporary, Is.False); | 405 | Assert.That(attSo.IsTemporary, Is.False); |
406 | |||
407 | // Check appearance status | ||
408 | Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1)); | ||
409 | Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest)); | ||
410 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | ||
411 | |||
412 | // Check events | ||
413 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); | ||
414 | } | ||
415 | |||
416 | // Test attaching an already attached attachment | ||
417 | { | ||
418 | scene.AttachmentsModule.RezSingleAttachmentFromInventory( | ||
419 | sp, attItem.ID, (uint)AttachmentPoint.Chest); | ||
292 | 420 | ||
293 | // Check appearance status | 421 | // Check scene presence status |
294 | Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1)); | 422 | Assert.That(sp.HasAttachments(), Is.True); |
295 | Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest)); | 423 | List<SceneObjectGroup> attachments = sp.GetAttachments(); |
424 | Assert.That(attachments.Count, Is.EqualTo(1)); | ||
425 | SceneObjectGroup attSo = attachments[0]; | ||
426 | Assert.That(attSo.Name, Is.EqualTo(attItem.Name)); | ||
427 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.Chest)); | ||
428 | Assert.That(attSo.IsAttachment); | ||
429 | Assert.That(attSo.UsesPhysics, Is.False); | ||
430 | Assert.That(attSo.IsTemporary, Is.False); | ||
431 | |||
432 | // Check appearance status | ||
433 | Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1)); | ||
434 | Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest)); | ||
435 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | ||
436 | |||
437 | // Check events | ||
438 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); | ||
439 | } | ||
440 | } | ||
296 | 441 | ||
297 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | 442 | /// <summary> |
443 | /// Test wearing an attachment from inventory, as opposed to explicit choosing the rez point | ||
444 | /// </summary> | ||
445 | [Test] | ||
446 | public void TestWearAttachmentFromInventory() | ||
447 | { | ||
448 | TestHelpers.InMethod(); | ||
449 | // TestHelpers.EnableLogging(); | ||
298 | 450 | ||
299 | // Check events | 451 | Scene scene = CreateTestScene(); |
300 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); | 452 | UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1); |
453 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1.PrincipalID); | ||
454 | |||
455 | InventoryItemBase attItem1 = CreateAttachmentItem(scene, ua1.PrincipalID, "att1", 0x10, 0x20); | ||
456 | InventoryItemBase attItem2 = CreateAttachmentItem(scene, ua1.PrincipalID, "att2", 0x11, 0x21); | ||
457 | |||
458 | { | ||
459 | m_numberOfAttachEventsFired = 0; | ||
460 | scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, attItem1.ID, (uint)AttachmentPoint.Default); | ||
461 | |||
462 | // default attachment point is currently the left hand. | ||
463 | Assert.That(sp.HasAttachments(), Is.True); | ||
464 | List<SceneObjectGroup> attachments = sp.GetAttachments(); | ||
465 | Assert.That(attachments.Count, Is.EqualTo(1)); | ||
466 | SceneObjectGroup attSo = attachments[0]; | ||
467 | Assert.That(attSo.Name, Is.EqualTo(attItem1.Name)); | ||
468 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand)); | ||
469 | Assert.That(attSo.IsAttachment); | ||
470 | |||
471 | // Check appearance status | ||
472 | Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1)); | ||
473 | Assert.That(sp.Appearance.GetAttachpoint(attItem1.ID), Is.EqualTo((int)AttachmentPoint.LeftHand)); | ||
474 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | ||
475 | |||
476 | // Check events | ||
477 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); | ||
478 | } | ||
479 | |||
480 | // Test wearing a second attachment at the same position | ||
481 | // Until multiple attachments at one point is implemented, this will remove the first attachment | ||
482 | // This test relies on both attachments having the same default attachment point (in this case LeftHand | ||
483 | // since none other has been set). | ||
484 | { | ||
485 | scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, attItem2.ID, (uint)AttachmentPoint.Default); | ||
486 | |||
487 | // default attachment point is currently the left hand. | ||
488 | Assert.That(sp.HasAttachments(), Is.True); | ||
489 | List<SceneObjectGroup> attachments = sp.GetAttachments(); | ||
490 | Assert.That(attachments.Count, Is.EqualTo(1)); | ||
491 | SceneObjectGroup attSo = attachments[0]; | ||
492 | Assert.That(attSo.Name, Is.EqualTo(attItem2.Name)); | ||
493 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand)); | ||
494 | Assert.That(attSo.IsAttachment); | ||
495 | |||
496 | // Check appearance status | ||
497 | Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1)); | ||
498 | Assert.That(sp.Appearance.GetAttachpoint(attItem2.ID), Is.EqualTo((int)AttachmentPoint.LeftHand)); | ||
499 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | ||
500 | |||
501 | // Check events | ||
502 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3)); | ||
503 | } | ||
504 | |||
505 | // Test wearing an already attached attachment | ||
506 | { | ||
507 | scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, attItem2.ID, (uint)AttachmentPoint.Default); | ||
508 | |||
509 | // default attachment point is currently the left hand. | ||
510 | Assert.That(sp.HasAttachments(), Is.True); | ||
511 | List<SceneObjectGroup> attachments = sp.GetAttachments(); | ||
512 | Assert.That(attachments.Count, Is.EqualTo(1)); | ||
513 | SceneObjectGroup attSo = attachments[0]; | ||
514 | Assert.That(attSo.Name, Is.EqualTo(attItem2.Name)); | ||
515 | Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand)); | ||
516 | Assert.That(attSo.IsAttachment); | ||
517 | |||
518 | // Check appearance status | ||
519 | Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1)); | ||
520 | Assert.That(sp.Appearance.GetAttachpoint(attItem2.ID), Is.EqualTo((int)AttachmentPoint.LeftHand)); | ||
521 | Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); | ||
522 | |||
523 | // Check events | ||
524 | Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3)); | ||
525 | } | ||
301 | } | 526 | } |
302 | 527 | ||
303 | /// <summary> | 528 | /// <summary> |
diff --git a/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs index 37131b9..1f1568f 100644 --- a/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs +++ b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs | |||
@@ -39,7 +39,7 @@ using OpenSim.Region.Framework; | |||
39 | using OpenSim.Region.Framework.Interfaces; | 39 | using OpenSim.Region.Framework.Interfaces; |
40 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
41 | 41 | ||
42 | namespace OpenSim.Region.Framework.DynamicAttributes.DAExampleModule | 42 | namespace OpenSim.Region.CoreModules.Framework.DynamicAttributes.DAExampleModule |
43 | { | 43 | { |
44 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DAExampleModule")] | 44 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DAExampleModule")] |
45 | public class DAExampleModule : INonSharedRegionModule | 45 | public class DAExampleModule : INonSharedRegionModule |
@@ -48,6 +48,8 @@ namespace OpenSim.Region.Framework.DynamicAttributes.DAExampleModule | |||
48 | 48 | ||
49 | private static readonly bool ENABLED = false; // enable for testing | 49 | private static readonly bool ENABLED = false; // enable for testing |
50 | 50 | ||
51 | public const string DANamespace = "DAExample Module"; | ||
52 | |||
51 | protected Scene m_scene; | 53 | protected Scene m_scene; |
52 | protected IDialogModule m_dialogMod; | 54 | protected IDialogModule m_dialogMod; |
53 | 55 | ||
@@ -85,19 +87,29 @@ namespace OpenSim.Region.Framework.DynamicAttributes.DAExampleModule | |||
85 | { | 87 | { |
86 | OSDMap attrs = null; | 88 | OSDMap attrs = null; |
87 | SceneObjectPart sop = m_scene.GetSceneObjectPart(groupId); | 89 | SceneObjectPart sop = m_scene.GetSceneObjectPart(groupId); |
88 | if (!sop.DynAttrs.TryGetValue(Name, out attrs)) | 90 | |
91 | if (sop == null) | ||
92 | return true; | ||
93 | |||
94 | if (!sop.DynAttrs.TryGetValue(DANamespace, out attrs)) | ||
89 | attrs = new OSDMap(); | 95 | attrs = new OSDMap(); |
90 | 96 | ||
91 | OSDInteger newValue; | 97 | OSDInteger newValue; |
92 | |||
93 | if (!attrs.ContainsKey("moves")) | ||
94 | newValue = new OSDInteger(1); | ||
95 | else | ||
96 | newValue = new OSDInteger(((OSDInteger)attrs["moves"]).AsInteger() + 1); | ||
97 | |||
98 | attrs["moves"] = newValue; | ||
99 | 98 | ||
100 | sop.DynAttrs[Name] = attrs; | 99 | // We have to lock on the entire dynamic attributes map to avoid race conditions with serialization code. |
100 | lock (sop.DynAttrs) | ||
101 | { | ||
102 | if (!attrs.ContainsKey("moves")) | ||
103 | newValue = new OSDInteger(1); | ||
104 | else | ||
105 | newValue = new OSDInteger(attrs["moves"].AsInteger() + 1); | ||
106 | |||
107 | attrs["moves"] = newValue; | ||
108 | |||
109 | sop.DynAttrs[DANamespace] = attrs; | ||
110 | } | ||
111 | |||
112 | sop.ParentGroup.HasGroupChanged = true; | ||
101 | 113 | ||
102 | m_dialogMod.SendGeneralAlert(string.Format("{0} {1} moved {2} times", sop.Name, sop.UUID, newValue)); | 114 | m_dialogMod.SendGeneralAlert(string.Format("{0} {1} moved {2} times", sop.Name, sop.UUID, newValue)); |
103 | 115 | ||
diff --git a/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DOExampleModule.cs b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DOExampleModule.cs new file mode 100644 index 0000000..650aa35 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/DynamicAttributes/DOExampleModule.cs | |||
@@ -0,0 +1,139 @@ | |||
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.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using log4net; | ||
32 | using Mono.Addins; | ||
33 | using Nini.Config; | ||
34 | using OpenMetaverse; | ||
35 | using OpenMetaverse.Packets; | ||
36 | using OpenMetaverse.StructuredData; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Region.Framework; | ||
39 | using OpenSim.Region.CoreModules.Framework.DynamicAttributes.DAExampleModule; | ||
40 | using OpenSim.Region.Framework.Interfaces; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | |||
43 | namespace OpenSim.Region.Framework.DynamicAttributes.DOExampleModule | ||
44 | { | ||
45 | /// <summary> | ||
46 | /// Example module for experimenting with and demonstrating dynamic object ideas. | ||
47 | /// </summary> | ||
48 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DOExampleModule")] | ||
49 | public class DOExampleModule : INonSharedRegionModule | ||
50 | { | ||
51 | public class MyObject | ||
52 | { | ||
53 | public int Moves { get; set; } | ||
54 | |||
55 | public MyObject(int moves) | ||
56 | { | ||
57 | Moves = moves; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
62 | |||
63 | private static readonly bool ENABLED = false; // enable for testing | ||
64 | |||
65 | private Scene m_scene; | ||
66 | private IDialogModule m_dialogMod; | ||
67 | |||
68 | public string Name { get { return "DOExample Module"; } } | ||
69 | public Type ReplaceableInterface { get { return null; } } | ||
70 | |||
71 | public void Initialise(IConfigSource source) {} | ||
72 | |||
73 | public void AddRegion(Scene scene) | ||
74 | { | ||
75 | if (ENABLED) | ||
76 | { | ||
77 | m_scene = scene; | ||
78 | m_scene.EventManager.OnObjectAddedToScene += OnObjectAddedToScene; | ||
79 | m_scene.EventManager.OnSceneGroupMove += OnSceneGroupMove; | ||
80 | m_dialogMod = m_scene.RequestModuleInterface<IDialogModule>(); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | public void RemoveRegion(Scene scene) | ||
85 | { | ||
86 | if (ENABLED) | ||
87 | { | ||
88 | m_scene.EventManager.OnSceneGroupMove -= OnSceneGroupMove; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | public void RegionLoaded(Scene scene) {} | ||
93 | |||
94 | public void Close() | ||
95 | { | ||
96 | RemoveRegion(m_scene); | ||
97 | } | ||
98 | |||
99 | private void OnObjectAddedToScene(SceneObjectGroup so) | ||
100 | { | ||
101 | SceneObjectPart rootPart = so.RootPart; | ||
102 | |||
103 | OSDMap attrs; | ||
104 | |||
105 | int movesSoFar = 0; | ||
106 | |||
107 | // Console.WriteLine("Here for {0}", so.Name); | ||
108 | |||
109 | if (rootPart.DynAttrs.TryGetValue(DAExampleModule.DANamespace, out attrs)) | ||
110 | { | ||
111 | movesSoFar = attrs["moves"].AsInteger(); | ||
112 | |||
113 | m_log.DebugFormat( | ||
114 | "[DO EXAMPLE MODULE]: Found saved moves {0} for {1} in {2}", movesSoFar, so.Name, m_scene.Name); | ||
115 | } | ||
116 | |||
117 | rootPart.DynObjs.Add(Name, new MyObject(movesSoFar)); | ||
118 | } | ||
119 | |||
120 | private bool OnSceneGroupMove(UUID groupId, Vector3 delta) | ||
121 | { | ||
122 | SceneObjectGroup so = m_scene.GetSceneObjectGroup(groupId); | ||
123 | |||
124 | if (so == null) | ||
125 | return true; | ||
126 | |||
127 | object rawObj = so.RootPart.DynObjs.Get(Name); | ||
128 | |||
129 | if (rawObj != null) | ||
130 | { | ||
131 | MyObject myObj = (MyObject)rawObj; | ||
132 | |||
133 | m_dialogMod.SendGeneralAlert(string.Format("{0} {1} moved {2} times", so.Name, so.UUID, ++myObj.Moves)); | ||
134 | } | ||
135 | |||
136 | return true; | ||
137 | } | ||
138 | } | ||
139 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 07c3666..9b1b69a 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs | |||
@@ -66,6 +66,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
66 | /// </summary> | 66 | /// </summary> |
67 | public bool WaitForAgentArrivedAtDestination { get; set; } | 67 | public bool WaitForAgentArrivedAtDestination { get; set; } |
68 | 68 | ||
69 | /// <summary> | ||
70 | /// If true then we ask the viewer to disable teleport cancellation and ignore teleport requests. | ||
71 | /// </summary> | ||
72 | /// <remarks> | ||
73 | /// This is useful in situations where teleport is very likely to always succeed and we want to avoid a | ||
74 | /// situation where avatars can be come 'stuck' due to a failed teleport cancellation. Unfortunately, the | ||
75 | /// nature of the teleport protocol makes it extremely difficult (maybe impossible) to make teleport | ||
76 | /// cancellation consistently suceed. | ||
77 | /// </remarks> | ||
78 | public bool DisableInterRegionTeleportCancellation { get; set; } | ||
79 | |||
69 | protected bool m_Enabled = false; | 80 | protected bool m_Enabled = false; |
70 | 81 | ||
71 | public Scene Scene { get; private set; } | 82 | public Scene Scene { get; private set; } |
@@ -116,6 +127,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
116 | IConfig transferConfig = source.Configs["EntityTransfer"]; | 127 | IConfig transferConfig = source.Configs["EntityTransfer"]; |
117 | if (transferConfig != null) | 128 | if (transferConfig != null) |
118 | { | 129 | { |
130 | DisableInterRegionTeleportCancellation | ||
131 | = transferConfig.GetBoolean("DisableInterRegionTeleportCancellation", false); | ||
132 | |||
119 | WaitForAgentArrivedAtDestination | 133 | WaitForAgentArrivedAtDestination |
120 | = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault); | 134 | = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault); |
121 | 135 | ||
@@ -150,6 +164,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
150 | { | 164 | { |
151 | client.OnTeleportHomeRequest += TeleportHome; | 165 | client.OnTeleportHomeRequest += TeleportHome; |
152 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; | 166 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; |
167 | |||
168 | if (!DisableInterRegionTeleportCancellation) | ||
169 | client.OnTeleportCancel += OnClientCancelTeleport; | ||
153 | } | 170 | } |
154 | 171 | ||
155 | public virtual void Close() {} | 172 | public virtual void Close() {} |
@@ -168,6 +185,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
168 | 185 | ||
169 | #region Agent Teleports | 186 | #region Agent Teleports |
170 | 187 | ||
188 | private void OnClientCancelTeleport(IClientAPI client) | ||
189 | { | ||
190 | m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Cancelling); | ||
191 | |||
192 | m_log.DebugFormat( | ||
193 | "[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name); | ||
194 | } | ||
195 | |||
171 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) | 196 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) |
172 | { | 197 | { |
173 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) | 198 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) |
@@ -519,6 +544,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
519 | if (sp.ParentID != (uint)0) | 544 | if (sp.ParentID != (uint)0) |
520 | sp.StandUp(); | 545 | sp.StandUp(); |
521 | 546 | ||
547 | if (DisableInterRegionTeleportCancellation) | ||
548 | teleportFlags |= (uint)TeleportFlags.DisableCancel; | ||
549 | |||
522 | // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to | 550 | // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to |
523 | // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested). | 551 | // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested). |
524 | sp.ControllingClient.SendTeleportStart(teleportFlags); | 552 | sp.ControllingClient.SendTeleportStart(teleportFlags); |
@@ -567,6 +595,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
567 | return; | 595 | return; |
568 | } | 596 | } |
569 | 597 | ||
598 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) | ||
599 | { | ||
600 | m_log.DebugFormat( | ||
601 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", | ||
602 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
603 | |||
604 | return; | ||
605 | } | ||
606 | |||
570 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. | 607 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. |
571 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | 608 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); |
572 | 609 | ||
@@ -631,7 +668,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
631 | return; | 668 | return; |
632 | } | 669 | } |
633 | 670 | ||
634 | sp.ControllingClient.SendTeleportProgress(teleportFlags | (uint)TeleportFlags.DisableCancel, "sending_dest"); | 671 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) |
672 | { | ||
673 | m_log.DebugFormat( | ||
674 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", | ||
675 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
676 | |||
677 | CleanupAbortedInterRegionTeleport(sp, finalDestination); | ||
678 | |||
679 | return; | ||
680 | } | ||
635 | 681 | ||
636 | m_log.DebugFormat( | 682 | m_log.DebugFormat( |
637 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", | 683 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", |
@@ -714,14 +760,19 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
714 | // } | 760 | // } |
715 | } | 761 | } |
716 | 762 | ||
717 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) | 763 | /// <summary> |
764 | /// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation. | ||
765 | /// </summary> | ||
766 | /// <remarks> | ||
767 | /// All operations here must be idempotent so that we can call this method at any point in the teleport process | ||
768 | /// up until we send the TeleportFinish event quene event to the viewer. | ||
769 | /// <remarks> | ||
770 | /// <param name='sp'> </param> | ||
771 | /// <param name='finalDestination'></param> | ||
772 | protected virtual void CleanupAbortedInterRegionTeleport(ScenePresence sp, GridRegion finalDestination) | ||
718 | { | 773 | { |
719 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 774 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
720 | 775 | ||
721 | // Client never contacted destination. Let's restore everything back | ||
722 | sp.ControllingClient.SendTeleportFailed("Problems connecting to destination."); | ||
723 | |||
724 | // Fail. Reset it back | ||
725 | sp.IsChildAgent = false; | 776 | sp.IsChildAgent = false; |
726 | ReInstantiateScripts(sp); | 777 | ReInstantiateScripts(sp); |
727 | 778 | ||
@@ -729,7 +780,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
729 | 780 | ||
730 | // Finally, kill the agent we just created at the destination. | 781 | // Finally, kill the agent we just created at the destination. |
731 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID); | 782 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID); |
783 | } | ||
732 | 784 | ||
785 | /// <summary> | ||
786 | /// Signal that the inter-region teleport failed and perform cleanup. | ||
787 | /// </summary> | ||
788 | /// <param name='sp'></param> | ||
789 | /// <param name='finalDestination'></param> | ||
790 | /// <param name='logout'></param> | ||
791 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) | ||
792 | { | ||
793 | CleanupAbortedInterRegionTeleport(sp, finalDestination); | ||
794 | |||
795 | sp.ControllingClient.SendTeleportFailed( | ||
796 | string.Format("Problems connecting to destination {0}", finalDestination.RegionName)); | ||
733 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); | 797 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); |
734 | } | 798 | } |
735 | 799 | ||
@@ -1206,6 +1270,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1206 | // region doesn't take it | 1270 | // region doesn't take it |
1207 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); | 1271 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); |
1208 | 1272 | ||
1273 | m_log.WarnFormat( | ||
1274 | "[ENTITY TRANSFER MODULE]: Region {0} would not accept update for agent {1} on cross attempt. Returning to original region.", | ||
1275 | neighbourRegion.RegionName, agent.Name); | ||
1276 | |||
1209 | ReInstantiateScripts(agent); | 1277 | ReInstantiateScripts(agent); |
1210 | agent.AddToPhysicalScene(isFlying); | 1278 | agent.AddToPhysicalScene(isFlying); |
1211 | 1279 | ||
@@ -1225,6 +1293,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1225 | neighbourRegion.RegionHandle); | 1293 | neighbourRegion.RegionHandle); |
1226 | return agent; | 1294 | return agent; |
1227 | } | 1295 | } |
1296 | |||
1228 | // No turning back | 1297 | // No turning back |
1229 | agent.IsChildAgent = true; | 1298 | agent.IsChildAgent = true; |
1230 | 1299 | ||
@@ -2092,7 +2161,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
2092 | 2161 | ||
2093 | public bool IsInTransit(UUID id) | 2162 | public bool IsInTransit(UUID id) |
2094 | { | 2163 | { |
2095 | return m_entityTransferStateMachine.IsInTransit(id); | 2164 | return m_entityTransferStateMachine.GetAgentTransferState(id) != null; |
2096 | } | 2165 | } |
2097 | 2166 | ||
2098 | protected void ReInstantiateScripts(ScenePresence sp) | 2167 | protected void ReInstantiateScripts(ScenePresence sp) |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs index d0cab49..24d81d9 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs | |||
@@ -51,8 +51,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
51 | /// This is a state machine. | 51 | /// This is a state machine. |
52 | /// | 52 | /// |
53 | /// [Entry] => Preparing | 53 | /// [Entry] => Preparing |
54 | /// Preparing => { Transferring || CleaningUp || [Exit] } | 54 | /// Preparing => { Transferring || Cancelling || CleaningUp || [Exit] } |
55 | /// Transferring => { ReceivedAtDestination || CleaningUp } | 55 | /// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp } |
56 | /// Cancelling => CleaningUp | ||
56 | /// ReceivedAtDestination => CleaningUp | 57 | /// ReceivedAtDestination => CleaningUp |
57 | /// CleaningUp => [Exit] | 58 | /// CleaningUp => [Exit] |
58 | /// | 59 | /// |
@@ -64,7 +65,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
64 | Preparing, // The agent is being prepared for transfer | 65 | Preparing, // The agent is being prepared for transfer |
65 | Transferring, // The agent is in the process of being transferred to a destination | 66 | Transferring, // The agent is in the process of being transferred to a destination |
66 | ReceivedAtDestination, // The destination has notified us that the agent has been successfully received | 67 | ReceivedAtDestination, // The destination has notified us that the agent has been successfully received |
67 | CleaningUp // The agent is being changed to child/removed after a transfer | 68 | CleaningUp, // The agent is being changed to child/removed after a transfer |
69 | Cancelling // The user has cancelled the teleport but we have yet to act upon this. | ||
68 | } | 70 | } |
69 | 71 | ||
70 | /// <summary> | 72 | /// <summary> |
@@ -115,42 +117,110 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
115 | /// <param name='newState'></param> | 117 | /// <param name='newState'></param> |
116 | /// <returns></returns> | 118 | /// <returns></returns> |
117 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> | 119 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> |
118 | internal void UpdateInTransit(UUID id, AgentTransferState newState) | 120 | internal bool UpdateInTransit(UUID id, AgentTransferState newState) |
119 | { | 121 | { |
122 | bool transitionOkay = false; | ||
123 | |||
124 | // We don't want to throw an exception on cancel since this can come it at any time. | ||
125 | bool failIfNotOkay = true; | ||
126 | |||
127 | // Should be a failure message if failure is not okay. | ||
128 | string failureMessage = null; | ||
129 | |||
130 | AgentTransferState? oldState = null; | ||
131 | |||
120 | lock (m_agentsInTransit) | 132 | lock (m_agentsInTransit) |
121 | { | 133 | { |
122 | // Illegal to try and update an agent that's not actually in transit. | 134 | // Illegal to try and update an agent that's not actually in transit. |
123 | if (!m_agentsInTransit.ContainsKey(id)) | 135 | if (!m_agentsInTransit.ContainsKey(id)) |
124 | throw new Exception( | 136 | { |
125 | string.Format( | 137 | if (newState != AgentTransferState.Cancelling) |
126 | "Agent with ID {0} is not registered as in transit in {1}", | 138 | failureMessage = string.Format( |
127 | id, m_mod.Scene.RegionInfo.RegionName)); | 139 | "Agent with ID {0} is not registered as in transit in {1}", |
128 | 140 | id, m_mod.Scene.RegionInfo.RegionName); | |
129 | AgentTransferState oldState = m_agentsInTransit[id]; | 141 | else |
142 | failIfNotOkay = false; | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | oldState = m_agentsInTransit[id]; | ||
130 | 147 | ||
131 | bool transitionOkay = false; | 148 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) |
149 | { | ||
150 | transitionOkay = true; | ||
151 | } | ||
152 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | ||
153 | { | ||
154 | transitionOkay = true; | ||
155 | } | ||
156 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | ||
157 | { | ||
158 | transitionOkay = true; | ||
159 | } | ||
160 | else | ||
161 | { | ||
162 | if (newState == AgentTransferState.Cancelling | ||
163 | && (oldState == AgentTransferState.Preparing || oldState == AgentTransferState.Transferring)) | ||
164 | { | ||
165 | transitionOkay = true; | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | failIfNotOkay = false; | ||
170 | } | ||
171 | } | ||
132 | 172 | ||
133 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | 173 | if (!transitionOkay) |
134 | transitionOkay = true; | 174 | failureMessage |
135 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | 175 | = string.Format( |
136 | transitionOkay = true; | 176 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}", |
137 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | 177 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName); |
138 | transitionOkay = true; | 178 | } |
139 | 179 | ||
140 | if (transitionOkay) | 180 | if (transitionOkay) |
181 | { | ||
141 | m_agentsInTransit[id] = newState; | 182 | m_agentsInTransit[id] = newState; |
142 | else | 183 | |
143 | throw new Exception( | 184 | // m_log.DebugFormat( |
144 | string.Format( | 185 | // "[ENTITY TRANSFER STATE MACHINE]: Changed agent with id {0} from state {1} to {2} in {3}", |
145 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}", | 186 | // id, oldState, newState, m_mod.Scene.Name); |
146 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName)); | 187 | } |
188 | else if (failIfNotOkay) | ||
189 | { | ||
190 | throw new Exception(failureMessage); | ||
191 | } | ||
192 | // else | ||
193 | // { | ||
194 | // if (oldState != null) | ||
195 | // m_log.DebugFormat( | ||
196 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} from state {1} to {2} in {3}", | ||
197 | // id, oldState, newState, m_mod.Scene.Name); | ||
198 | // else | ||
199 | // m_log.DebugFormat( | ||
200 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} to state {1} in {2} since agent not in transit", | ||
201 | // id, newState, m_mod.Scene.Name); | ||
202 | // } | ||
147 | } | 203 | } |
204 | |||
205 | return transitionOkay; | ||
148 | } | 206 | } |
149 | 207 | ||
150 | internal bool IsInTransit(UUID id) | 208 | /// <summary> |
209 | /// Gets the current agent transfer state. | ||
210 | /// </summary> | ||
211 | /// <returns>Null if the agent is not in transit</returns> | ||
212 | /// <param name='id'> | ||
213 | /// Identifier. | ||
214 | /// </param> | ||
215 | internal AgentTransferState? GetAgentTransferState(UUID id) | ||
151 | { | 216 | { |
152 | lock (m_agentsInTransit) | 217 | lock (m_agentsInTransit) |
153 | return m_agentsInTransit.ContainsKey(id); | 218 | { |
219 | if (!m_agentsInTransit.ContainsKey(id)) | ||
220 | return null; | ||
221 | else | ||
222 | return m_agentsInTransit[id]; | ||
223 | } | ||
154 | } | 224 | } |
155 | 225 | ||
156 | /// <summary> | 226 | /// <summary> |
@@ -203,14 +273,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
203 | 273 | ||
204 | lock (m_agentsInTransit) | 274 | lock (m_agentsInTransit) |
205 | { | 275 | { |
206 | if (!IsInTransit(id)) | 276 | AgentTransferState? currentState = GetAgentTransferState(id); |
277 | |||
278 | if (currentState == null) | ||
207 | throw new Exception( | 279 | throw new Exception( |
208 | string.Format( | 280 | string.Format( |
209 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit", | 281 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit", |
210 | id, m_mod.Scene.RegionInfo.RegionName)); | 282 | id, m_mod.Scene.RegionInfo.RegionName)); |
211 | 283 | ||
212 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
213 | |||
214 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) | 284 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) |
215 | throw new Exception( | 285 | throw new Exception( |
216 | string.Format( | 286 | string.Format( |
diff --git a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs index 4c9ee06..64feec1 100644 --- a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs | |||
@@ -414,8 +414,6 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring | |||
414 | } | 414 | } |
415 | private void RegisterStatsManagerRegionStatistics() | 415 | private void RegisterStatsManagerRegionStatistics() |
416 | { | 416 | { |
417 | string regionName = m_scene.RegionInfo.RegionName; | ||
418 | |||
419 | MakeStat("RootAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetRootAgentCount(); }); | 417 | MakeStat("RootAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetRootAgentCount(); }); |
420 | MakeStat("ChildAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetChildAgentCount(); }); | 418 | MakeStat("ChildAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetChildAgentCount(); }); |
421 | MakeStat("TotalPrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetTotalObjectsCount(); }); | 419 | MakeStat("TotalPrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetTotalObjectsCount(); }); |
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs index f04fabe..4cecd85 100644 --- a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs | |||
@@ -516,6 +516,9 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender | |||
516 | foreach (string line in GetLines(data, dataDelim)) | 516 | foreach (string line in GetLines(data, dataDelim)) |
517 | { | 517 | { |
518 | string nextLine = line.Trim(); | 518 | string nextLine = line.Trim(); |
519 | |||
520 | // m_log.DebugFormat("[VECTOR RENDER MODULE]: Processing line '{0}'", nextLine); | ||
521 | |||
519 | //replace with switch, or even better, do some proper parsing | 522 | //replace with switch, or even better, do some proper parsing |
520 | if (nextLine.StartsWith("MoveTo")) | 523 | if (nextLine.StartsWith("MoveTo")) |
521 | { | 524 | { |
@@ -829,6 +832,8 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender | |||
829 | float y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture); | 832 | float y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture); |
830 | PointF point = new PointF(x, y); | 833 | PointF point = new PointF(x, y); |
831 | points[i / 2] = point; | 834 | points[i / 2] = point; |
835 | |||
836 | // m_log.DebugFormat("[VECTOR RENDER MODULE]: Got point {0}", points[i / 2]); | ||
832 | } | 837 | } |
833 | } | 838 | } |
834 | } | 839 | } |
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs index 3c18074..a413546 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs | |||
@@ -219,12 +219,15 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation | |||
219 | { | 219 | { |
220 | // m_log.DebugFormat( | 220 | // m_log.DebugFormat( |
221 | // "[LOCAL SIMULATION CONNECTOR]: Found region {0} {1} to send AgentUpdate", | 221 | // "[LOCAL SIMULATION CONNECTOR]: Found region {0} {1} to send AgentUpdate", |
222 | // s.RegionInfo.RegionName, destination.RegionHandle); | 222 | // destination.RegionName, destination.RegionID); |
223 | 223 | ||
224 | return m_scenes[destination.RegionID].IncomingChildAgentDataUpdate(cAgentData); | 224 | return m_scenes[destination.RegionID].IncomingChildAgentDataUpdate(cAgentData); |
225 | } | 225 | } |
226 | 226 | ||
227 | // m_log.DebugFormat("[LOCAL COMMS]: Did not find region {0} for ChildAgentUpdate", regionHandle); | 227 | // m_log.DebugFormat( |
228 | // "[LOCAL COMMS]: Did not find region {0} {1} for ChildAgentUpdate", | ||
229 | // destination.RegionName, destination.RegionID); | ||
230 | |||
228 | return false; | 231 | return false; |
229 | } | 232 | } |
230 | 233 | ||
@@ -239,7 +242,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation | |||
239 | // note that we really don't need the GridRegion for this call | 242 | // note that we really don't need the GridRegion for this call |
240 | foreach (Scene s in m_scenes.Values) | 243 | foreach (Scene s in m_scenes.Values) |
241 | { | 244 | { |
242 | //m_log.Debug("[LOCAL COMMS]: Found region to send ChildAgentUpdate"); | 245 | // m_log.Debug("[LOCAL COMMS]: Found region to send ChildAgentUpdate"); |
243 | s.IncomingChildAgentDataUpdate(cAgentData); | 246 | s.IncomingChildAgentDataUpdate(cAgentData); |
244 | } | 247 | } |
245 | 248 | ||
diff --git a/OpenSim/Region/CoreModules/World/Land/LandChannel.cs b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs index 7fc358d..73c592d 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandChannel.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs | |||
@@ -95,6 +95,11 @@ namespace OpenSim.Region.CoreModules.World.Land | |||
95 | return null; | 95 | return null; |
96 | } | 96 | } |
97 | 97 | ||
98 | public ILandObject GetLandObject(Vector3 position) | ||
99 | { | ||
100 | return GetLandObject(position.X, position.Y); | ||
101 | } | ||
102 | |||
98 | public ILandObject GetLandObject(int x, int y) | 103 | public ILandObject GetLandObject(int x, int y) |
99 | { | 104 | { |
100 | if (m_landManagementModule != null) | 105 | if (m_landManagementModule != null) |
diff --git a/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs b/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs index 345f01b..b67312e 100644 --- a/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs | |||
@@ -41,6 +41,16 @@ namespace OpenSim.Region.Framework.Interfaces | |||
41 | Value = 3 | 41 | Value = 3 |
42 | } | 42 | } |
43 | 43 | ||
44 | public enum JsonStoreValueType | ||
45 | { | ||
46 | Undefined = 0, | ||
47 | Boolean = 1, | ||
48 | Integer = 2, | ||
49 | Float = 3, | ||
50 | String = 4, | ||
51 | UUID = 5 | ||
52 | } | ||
53 | |||
44 | public delegate void TakeValueCallback(string s); | 54 | public delegate void TakeValueCallback(string s); |
45 | 55 | ||
46 | public interface IJsonStoreModule | 56 | public interface IJsonStoreModule |
@@ -49,7 +59,9 @@ namespace OpenSim.Region.Framework.Interfaces | |||
49 | bool CreateStore(string value, ref UUID result); | 59 | bool CreateStore(string value, ref UUID result); |
50 | bool DestroyStore(UUID storeID); | 60 | bool DestroyStore(UUID storeID); |
51 | 61 | ||
52 | JsonStoreNodeType GetPathType(UUID storeID, string path); | 62 | JsonStoreNodeType GetNodeType(UUID storeID, string path); |
63 | JsonStoreValueType GetValueType(UUID storeID, string path); | ||
64 | |||
53 | bool TestStore(UUID storeID); | 65 | bool TestStore(UUID storeID); |
54 | 66 | ||
55 | bool SetValue(UUID storeID, string path, string value, bool useJson); | 67 | bool SetValue(UUID storeID, string path, string value, bool useJson); |
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index cce8b21..a8b63fe 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | |||
@@ -129,6 +129,27 @@ namespace OpenSim.Region.Framework.Scenes | |||
129 | /// Dynamic attributes can be created and deleted as required. | 129 | /// Dynamic attributes can be created and deleted as required. |
130 | /// </summary> | 130 | /// </summary> |
131 | public DAMap DynAttrs { get; set; } | 131 | public DAMap DynAttrs { get; set; } |
132 | |||
133 | private DOMap m_dynObjs; | ||
134 | |||
135 | /// <summary> | ||
136 | /// Dynamic objects that can be created and deleted as required. | ||
137 | /// </summary> | ||
138 | public DOMap DynObjs | ||
139 | { | ||
140 | get | ||
141 | { | ||
142 | if (m_dynObjs == null) | ||
143 | m_dynObjs = new DOMap(); | ||
144 | |||
145 | return m_dynObjs; | ||
146 | } | ||
147 | |||
148 | set | ||
149 | { | ||
150 | m_dynObjs = value; | ||
151 | } | ||
152 | } | ||
132 | 153 | ||
133 | /// <value> | 154 | /// <value> |
134 | /// Is this a root part? | 155 | /// Is this a root part? |
@@ -4503,8 +4524,25 @@ namespace OpenSim.Region.Framework.Scenes | |||
4503 | 4524 | ||
4504 | Changed changeFlags = 0; | 4525 | Changed changeFlags = 0; |
4505 | 4526 | ||
4527 | Primitive.TextureEntryFace fallbackNewFace = newTex.DefaultTexture; | ||
4528 | Primitive.TextureEntryFace fallbackOldFace = oldTex.DefaultTexture; | ||
4529 | |||
4530 | // On Incoming packets, sometimes newText.DefaultTexture is null. The assumption is that all | ||
4531 | // other prim-sides are set, but apparently that's not always the case. Lets assume packet/data corruption at this point. | ||
4532 | if (fallbackNewFace == null) | ||
4533 | { | ||
4534 | fallbackNewFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0); | ||
4535 | newTex.DefaultTexture = fallbackNewFace; | ||
4536 | } | ||
4537 | if (fallbackOldFace == null) | ||
4538 | { | ||
4539 | fallbackOldFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0); | ||
4540 | oldTex.DefaultTexture = fallbackOldFace; | ||
4541 | } | ||
4542 | |||
4506 | for (int i = 0 ; i < GetNumberOfSides(); i++) | 4543 | for (int i = 0 ; i < GetNumberOfSides(); i++) |
4507 | { | 4544 | { |
4545 | |||
4508 | Primitive.TextureEntryFace newFace = newTex.DefaultTexture; | 4546 | Primitive.TextureEntryFace newFace = newTex.DefaultTexture; |
4509 | Primitive.TextureEntryFace oldFace = oldTex.DefaultTexture; | 4547 | Primitive.TextureEntryFace oldFace = oldTex.DefaultTexture; |
4510 | 4548 | ||
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 39a885c..82bb759 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs | |||
@@ -559,16 +559,28 @@ namespace OpenSim.Region.Framework.Scenes | |||
559 | 559 | ||
560 | private Quaternion m_bodyRot = Quaternion.Identity; | 560 | private Quaternion m_bodyRot = Quaternion.Identity; |
561 | 561 | ||
562 | /// <summary> | ||
563 | /// The rotation of the avatar. | ||
564 | /// </summary> | ||
565 | /// <remarks> | ||
566 | /// If the avatar is not sitting, this is with respect to the world | ||
567 | /// If the avatar is sitting, this is a with respect to the part that it's sitting upon (a local rotation). | ||
568 | /// If you always want the world rotation, use GetWorldRotation() | ||
569 | /// </remarks> | ||
562 | public Quaternion Rotation | 570 | public Quaternion Rotation |
563 | { | 571 | { |
564 | get { return m_bodyRot; } | 572 | get |
573 | { | ||
574 | return m_bodyRot; | ||
575 | } | ||
576 | |||
565 | set | 577 | set |
566 | { | 578 | { |
567 | m_bodyRot = value; | 579 | m_bodyRot = value; |
580 | |||
568 | if (PhysicsActor != null) | 581 | if (PhysicsActor != null) |
569 | { | ||
570 | PhysicsActor.Orientation = m_bodyRot; | 582 | PhysicsActor.Orientation = m_bodyRot; |
571 | } | 583 | |
572 | // m_log.DebugFormat("[SCENE PRESENCE]: Body rot for {0} set to {1}", Name, m_bodyRot); | 584 | // m_log.DebugFormat("[SCENE PRESENCE]: Body rot for {0} set to {1}", Name, m_bodyRot); |
573 | } | 585 | } |
574 | } | 586 | } |
@@ -608,6 +620,26 @@ namespace OpenSim.Region.Framework.Scenes | |||
608 | set { m_health = value; } | 620 | set { m_health = value; } |
609 | } | 621 | } |
610 | 622 | ||
623 | /// <summary> | ||
624 | /// Gets the world rotation of this presence. | ||
625 | /// </summary> | ||
626 | /// <remarks> | ||
627 | /// Unlike Rotation, this returns the world rotation no matter whether the avatar is sitting on a prim or not. | ||
628 | /// </remarks> | ||
629 | /// <returns></returns> | ||
630 | public Quaternion GetWorldRotation() | ||
631 | { | ||
632 | if (IsSatOnObject) | ||
633 | { | ||
634 | SceneObjectPart sitPart = ParentPart; | ||
635 | |||
636 | if (sitPart != null) | ||
637 | return sitPart.GetWorldRotation() * Rotation; | ||
638 | } | ||
639 | |||
640 | return Rotation; | ||
641 | } | ||
642 | |||
611 | public void AdjustKnownSeeds() | 643 | public void AdjustKnownSeeds() |
612 | { | 644 | { |
613 | Dictionary<ulong, string> seeds; | 645 | Dictionary<ulong, string> seeds; |
@@ -709,8 +741,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
709 | 741 | ||
710 | #endregion | 742 | #endregion |
711 | 743 | ||
712 | |||
713 | |||
714 | #region Constructor(s) | 744 | #region Constructor(s) |
715 | 745 | ||
716 | public ScenePresence( | 746 | public ScenePresence( |
@@ -1613,32 +1643,28 @@ namespace OpenSim.Region.Framework.Scenes | |||
1613 | bool controlland = (((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || | 1643 | bool controlland = (((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || |
1614 | ((flags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); | 1644 | ((flags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); |
1615 | 1645 | ||
1616 | |||
1617 | //m_log.Debug("[CONTROL]: " +flags); | 1646 | //m_log.Debug("[CONTROL]: " +flags); |
1618 | // Applies a satisfying roll effect to the avatar when flying. | 1647 | // Applies a satisfying roll effect to the avatar when flying. |
1619 | if (((flags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) != 0) && ((flags & AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS) != 0)) | 1648 | if ((flags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) != 0 && (flags & AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS) != 0) |
1620 | { | 1649 | { |
1621 | 1650 | ApplyFlyingRoll( | |
1622 | ApplyFlyingRoll(FLY_ROLL_RADIANS_PER_UPDATE, ((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0), ((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0)); | 1651 | FLY_ROLL_RADIANS_PER_UPDATE, |
1623 | 1652 | (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0, | |
1624 | 1653 | (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0); | |
1625 | } | 1654 | } |
1626 | else if (((flags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) != 0) && | 1655 | else if ((flags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) != 0 && |
1627 | ((flags & AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG) != 0)) | 1656 | (flags & AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG) != 0) |
1628 | { | 1657 | { |
1629 | ApplyFlyingRoll(-FLY_ROLL_RADIANS_PER_UPDATE, ((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0), ((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0)); | 1658 | ApplyFlyingRoll( |
1630 | 1659 | -FLY_ROLL_RADIANS_PER_UPDATE, | |
1631 | 1660 | (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0, | |
1661 | (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0); | ||
1632 | } | 1662 | } |
1633 | else | 1663 | else |
1634 | { | 1664 | { |
1635 | if (m_AngularVelocity.Z != 0) | 1665 | if (m_AngularVelocity.Z != 0) |
1636 | m_AngularVelocity.Z += CalculateFlyingRollResetToZero(FLY_ROLL_RESET_RADIANS_PER_UPDATE); | 1666 | m_AngularVelocity.Z += CalculateFlyingRollResetToZero(FLY_ROLL_RESET_RADIANS_PER_UPDATE); |
1637 | 1667 | } | |
1638 | } | ||
1639 | |||
1640 | |||
1641 | |||
1642 | 1668 | ||
1643 | if (Flying && IsColliding && controlland) | 1669 | if (Flying && IsColliding && controlland) |
1644 | { | 1670 | { |
@@ -2400,7 +2426,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
2400 | /// <param name="vec">The vector in which to move. This is relative to the rotation argument</param> | 2426 | /// <param name="vec">The vector in which to move. This is relative to the rotation argument</param> |
2401 | public void AddNewMovement(Vector3 vec) | 2427 | public void AddNewMovement(Vector3 vec) |
2402 | { | 2428 | { |
2403 | // m_log.DebugFormat("[SCENE PRESENCE]: Adding new movement {0} for {1}", vec, Name); | 2429 | // m_log.DebugFormat( |
2430 | // "[SCENE PRESENCE]: Adding new movement {0} with rotation {1} for {2}", vec, Rotation, Name); | ||
2404 | 2431 | ||
2405 | Vector3 direc = vec * Rotation; | 2432 | Vector3 direc = vec * Rotation; |
2406 | direc.Normalize(); | 2433 | direc.Normalize(); |
@@ -2420,6 +2447,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
2420 | 2447 | ||
2421 | direc *= 0.03f * 128f * SpeedModifier; | 2448 | direc *= 0.03f * 128f * SpeedModifier; |
2422 | 2449 | ||
2450 | // m_log.DebugFormat("[SCENE PRESENCE]: Force to apply before modification was {0} for {1}", direc, Name); | ||
2451 | |||
2423 | if (PhysicsActor != null) | 2452 | if (PhysicsActor != null) |
2424 | { | 2453 | { |
2425 | if (Flying) | 2454 | if (Flying) |
@@ -2453,6 +2482,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
2453 | } | 2482 | } |
2454 | } | 2483 | } |
2455 | 2484 | ||
2485 | // m_log.DebugFormat("[SCENE PRESENCE]: Setting force to apply to {0} for {1}", direc, Name); | ||
2486 | |||
2456 | // TODO: Add the force instead of only setting it to support multiple forces per frame? | 2487 | // TODO: Add the force instead of only setting it to support multiple forces per frame? |
2457 | m_forceToApply = direc; | 2488 | m_forceToApply = direc; |
2458 | } | 2489 | } |
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectUndoRedoTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectUndoRedoTests.cs index 96973de..4883ae7 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectUndoRedoTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectUndoRedoTests.cs | |||
@@ -110,8 +110,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests | |||
110 | 110 | ||
111 | Vector3 firstSize = new Vector3(2, 3, 4); | 111 | Vector3 firstSize = new Vector3(2, 3, 4); |
112 | Vector3 secondSize = new Vector3(5, 6, 7); | 112 | Vector3 secondSize = new Vector3(5, 6, 7); |
113 | Vector3 thirdSize = new Vector3(8, 9, 10); | 113 | // Vector3 thirdSize = new Vector3(8, 9, 10); |
114 | Vector3 fourthSize = new Vector3(11, 12, 13); | 114 | // Vector3 fourthSize = new Vector3(11, 12, 13); |
115 | 115 | ||
116 | Scene scene = new SceneHelpers().SetupScene(); | 116 | Scene scene = new SceneHelpers().SetupScene(); |
117 | scene.MaxUndoCount = 20; | 117 | scene.MaxUndoCount = 20; |
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs index 5faf131..bbfbbfc 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs | |||
@@ -289,108 +289,5 @@ namespace OpenSim.Region.Framework.Scenes.Tests | |||
289 | // | 289 | // |
290 | // Assert.That(presence, Is.Null, "presence is not null"); | 290 | // Assert.That(presence, Is.Null, "presence is not null"); |
291 | // } | 291 | // } |
292 | |||
293 | // I'm commenting this test because it does not represent | ||
294 | // crossings. The Thread.Sleep's in here are not meaningful mocks, | ||
295 | // and they sometimes fail in panda. | ||
296 | // We need to talk in order to develop a test | ||
297 | // that really tests region crossings. There are 3 async components, | ||
298 | // but things are synchronous among them. So there should be | ||
299 | // 3 threads in here. | ||
300 | //[Test] | ||
301 | // public void T021_TestCrossToNewRegion() | ||
302 | // { | ||
303 | // TestHelpers.InMethod(); | ||
304 | // | ||
305 | // scene.RegisterRegionWithGrid(); | ||
306 | // scene2.RegisterRegionWithGrid(); | ||
307 | // | ||
308 | // // Adding child agent to region 1001 | ||
309 | // string reason; | ||
310 | // scene2.NewUserConnection(acd1,0, out reason); | ||
311 | // scene2.AddNewClient(testclient, PresenceType.User); | ||
312 | // | ||
313 | // ScenePresence presence = scene.GetScenePresence(agent1); | ||
314 | // presence.MakeRootAgent(new Vector3(0,unchecked(Constants.RegionSize-1),0), true); | ||
315 | // | ||
316 | // ScenePresence presence2 = scene2.GetScenePresence(agent1); | ||
317 | // | ||
318 | // // Adding neighbour region caps info to presence2 | ||
319 | // | ||
320 | // string cap = presence.ControllingClient.RequestClientInfo().CapsPath; | ||
321 | // presence2.AddNeighbourRegion(region1, cap); | ||
322 | // | ||
323 | // Assert.That(presence.IsChildAgent, Is.False, "Did not start root in origin region."); | ||
324 | // Assert.That(presence2.IsChildAgent, Is.True, "Is not a child on destination region."); | ||
325 | // | ||
326 | // // Cross to x+1 | ||
327 | // presence.AbsolutePosition = new Vector3(Constants.RegionSize+1,3,100); | ||
328 | // presence.Update(); | ||
329 | // | ||
330 | // EventWaitHandle wh = new EventWaitHandle (false, EventResetMode.AutoReset, "Crossing"); | ||
331 | // | ||
332 | // // Mimicking communication between client and server, by waiting OK from client | ||
333 | // // sent by TestClient.CrossRegion call. Originally, this is network comm. | ||
334 | // if (!wh.WaitOne(5000,false)) | ||
335 | // { | ||
336 | // presence.Update(); | ||
337 | // if (!wh.WaitOne(8000,false)) | ||
338 | // throw new ArgumentException("1 - Timeout waiting for signal/variable."); | ||
339 | // } | ||
340 | // | ||
341 | // // This is a TestClient specific method that fires OnCompleteMovementToRegion event, which | ||
342 | // // would normally be fired after receiving the reply packet from comm. done on the last line. | ||
343 | // testclient.CompleteMovement(); | ||
344 | // | ||
345 | // // Crossings are asynchronous | ||
346 | // int timer = 10; | ||
347 | // | ||
348 | // // Make sure cross hasn't already finished | ||
349 | // if (!presence.IsInTransit && !presence.IsChildAgent) | ||
350 | // { | ||
351 | // // If not and not in transit yet, give it some more time | ||
352 | // Thread.Sleep(5000); | ||
353 | // } | ||
354 | // | ||
355 | // // Enough time, should at least be in transit by now. | ||
356 | // while (presence.IsInTransit && timer > 0) | ||
357 | // { | ||
358 | // Thread.Sleep(1000); | ||
359 | // timer-=1; | ||
360 | // } | ||
361 | // | ||
362 | // Assert.That(timer,Is.GreaterThan(0),"Timed out waiting to cross 2->1."); | ||
363 | // Assert.That(presence.IsChildAgent, Is.True, "Did not complete region cross as expected."); | ||
364 | // Assert.That(presence2.IsChildAgent, Is.False, "Did not receive root status after receiving agent."); | ||
365 | // | ||
366 | // // Cross Back | ||
367 | // presence2.AbsolutePosition = new Vector3(-10, 3, 100); | ||
368 | // presence2.Update(); | ||
369 | // | ||
370 | // if (!wh.WaitOne(5000,false)) | ||
371 | // { | ||
372 | // presence2.Update(); | ||
373 | // if (!wh.WaitOne(8000,false)) | ||
374 | // throw new ArgumentException("2 - Timeout waiting for signal/variable."); | ||
375 | // } | ||
376 | // testclient.CompleteMovement(); | ||
377 | // | ||
378 | // if (!presence2.IsInTransit && !presence2.IsChildAgent) | ||
379 | // { | ||
380 | // // If not and not in transit yet, give it some more time | ||
381 | // Thread.Sleep(5000); | ||
382 | // } | ||
383 | // | ||
384 | // // Enough time, should at least be in transit by now. | ||
385 | // while (presence2.IsInTransit && timer > 0) | ||
386 | // { | ||
387 | // Thread.Sleep(1000); | ||
388 | // timer-=1; | ||
389 | // } | ||
390 | // | ||
391 | // Assert.That(timer,Is.GreaterThan(0),"Timed out waiting to cross 1->2."); | ||
392 | // Assert.That(presence2.IsChildAgent, Is.True, "Did not return from region as expected."); | ||
393 | // Assert.That(presence.IsChildAgent, Is.False, "Presence was not made root in old region again."); | ||
394 | // } | ||
395 | } | 292 | } |
396 | } \ No newline at end of file | 293 | } \ No newline at end of file |
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs new file mode 100644 index 0000000..81a2fcc --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs | |||
@@ -0,0 +1,157 @@ | |||
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.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using Nini.Config; | ||
32 | using NUnit.Framework; | ||
33 | using OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Framework.Communications; | ||
36 | using OpenSim.Framework.Servers; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | using OpenSim.Region.CoreModules.Framework; | ||
39 | using OpenSim.Region.CoreModules.Framework.EntityTransfer; | ||
40 | using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation; | ||
41 | using OpenSim.Tests.Common; | ||
42 | using OpenSim.Tests.Common.Mock; | ||
43 | |||
44 | namespace OpenSim.Region.Framework.Scenes.Tests | ||
45 | { | ||
46 | [TestFixture] | ||
47 | public class ScenePresenceCrossingTests : OpenSimTestCase | ||
48 | { | ||
49 | [TestFixtureSetUp] | ||
50 | public void FixtureInit() | ||
51 | { | ||
52 | // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread. | ||
53 | Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest; | ||
54 | } | ||
55 | |||
56 | [TestFixtureTearDown] | ||
57 | public void TearDown() | ||
58 | { | ||
59 | // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple | ||
60 | // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression | ||
61 | // tests really shouldn't). | ||
62 | Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; | ||
63 | } | ||
64 | |||
65 | [Test] | ||
66 | public void TestCrossOnSameSimulator() | ||
67 | { | ||
68 | TestHelpers.InMethod(); | ||
69 | // TestHelpers.EnableLogging(); | ||
70 | |||
71 | UUID userId = TestHelpers.ParseTail(0x1); | ||
72 | |||
73 | // TestEventQueueGetModule eqmA = new TestEventQueueGetModule(); | ||
74 | EntityTransferModule etmA = new EntityTransferModule(); | ||
75 | EntityTransferModule etmB = new EntityTransferModule(); | ||
76 | LocalSimulationConnectorModule lscm = new LocalSimulationConnectorModule(); | ||
77 | |||
78 | IConfigSource config = new IniConfigSource(); | ||
79 | IConfig modulesConfig = config.AddConfig("Modules"); | ||
80 | modulesConfig.Set("EntityTransferModule", etmA.Name); | ||
81 | modulesConfig.Set("SimulationServices", lscm.Name); | ||
82 | // IConfig entityTransferConfig = config.AddConfig("EntityTransfer"); | ||
83 | |||
84 | // In order to run a single threaded regression test we do not want the entity transfer module waiting | ||
85 | // for a callback from the destination scene before removing its avatar data. | ||
86 | // entityTransferConfig.Set("wait_for_callback", false); | ||
87 | |||
88 | SceneHelpers sh = new SceneHelpers(); | ||
89 | TestScene sceneA = sh.SetupScene("sceneA", TestHelpers.ParseTail(0x100), 1000, 1000); | ||
90 | TestScene sceneB = sh.SetupScene("sceneB", TestHelpers.ParseTail(0x200), 1000, 999); | ||
91 | |||
92 | SceneHelpers.SetupSceneModules(new Scene[] { sceneA, sceneB }, config, lscm); | ||
93 | SceneHelpers.SetupSceneModules(sceneA, config, new CapabilitiesModule(), etmA); | ||
94 | // SceneHelpers.SetupSceneModules(sceneA, config, new CapabilitiesModule(), etmA, eqmA); | ||
95 | SceneHelpers.SetupSceneModules(sceneB, config, new CapabilitiesModule(), etmB); | ||
96 | |||
97 | ScenePresence originalSp = SceneHelpers.AddScenePresence(sceneA, userId, sh.SceneManager); | ||
98 | originalSp.AbsolutePosition = new Vector3(128, 32, 10); | ||
99 | |||
100 | // originalSp.Flying = true; | ||
101 | |||
102 | // Console.WriteLine("First pos {0}", originalSp.AbsolutePosition); | ||
103 | |||
104 | // eqmA.ClearEvents(); | ||
105 | |||
106 | AgentUpdateArgs moveArgs = new AgentUpdateArgs(); | ||
107 | //moveArgs.BodyRotation = Quaternion.CreateFromEulers(Vector3.Zero); | ||
108 | moveArgs.BodyRotation = Quaternion.CreateFromEulers(new Vector3(0, 0, (float)-(Math.PI / 2))); | ||
109 | moveArgs.ControlFlags = (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_POS; | ||
110 | |||
111 | originalSp.HandleAgentUpdate(originalSp.ControllingClient, moveArgs); | ||
112 | |||
113 | sceneA.Update(1); | ||
114 | |||
115 | // Console.WriteLine("Second pos {0}", originalSp.AbsolutePosition); | ||
116 | |||
117 | // FIXME: This is a sufficient number of updates to for the presence to reach the northern border. | ||
118 | // But really we want to do this in a more robust way. | ||
119 | for (int i = 0; i < 100; i++) | ||
120 | { | ||
121 | sceneA.Update(1); | ||
122 | // Console.WriteLine("Pos {0}", originalSp.AbsolutePosition); | ||
123 | } | ||
124 | |||
125 | // Need to sort processing of EnableSimulator message on adding scene presences before we can test eqm | ||
126 | // messages | ||
127 | // Dictionary<UUID, List<TestEventQueueGetModule.Event>> eqmEvents = eqmA.Events; | ||
128 | // | ||
129 | // Assert.That(eqmEvents.Count, Is.EqualTo(1)); | ||
130 | // Assert.That(eqmEvents.ContainsKey(originalSp.UUID), Is.True); | ||
131 | // | ||
132 | // List<TestEventQueueGetModule.Event> spEqmEvents = eqmEvents[originalSp.UUID]; | ||
133 | // | ||
134 | // Assert.That(spEqmEvents.Count, Is.EqualTo(1)); | ||
135 | // Assert.That(spEqmEvents[0].Name, Is.EqualTo("CrossRegion")); | ||
136 | |||
137 | // sceneA should now only have a child agent | ||
138 | ScenePresence spAfterCrossSceneA = sceneA.GetScenePresence(originalSp.UUID); | ||
139 | Assert.That(spAfterCrossSceneA.IsChildAgent, Is.True); | ||
140 | |||
141 | ScenePresence spAfterCrossSceneB = sceneB.GetScenePresence(originalSp.UUID); | ||
142 | |||
143 | // Agent remains a child until the client triggers complete movement | ||
144 | Assert.That(spAfterCrossSceneB.IsChildAgent, Is.True); | ||
145 | |||
146 | TestClient sceneBTc = ((TestClient)spAfterCrossSceneB.ControllingClient); | ||
147 | |||
148 | int agentMovementCompleteReceived = 0; | ||
149 | sceneBTc.OnReceivedMoveAgentIntoRegion += (ri, pos, look) => agentMovementCompleteReceived++; | ||
150 | |||
151 | sceneBTc.CompleteMovement(); | ||
152 | |||
153 | Assert.That(agentMovementCompleteReceived, Is.EqualTo(1)); | ||
154 | Assert.That(spAfterCrossSceneB.IsChildAgent, Is.False); | ||
155 | } | ||
156 | } | ||
157 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs index a3d2436..6e74ce0 100644 --- a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs +++ b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs | |||
@@ -140,9 +140,12 @@ public class ServerStats : ISharedRegionModule | |||
140 | } | 140 | } |
141 | #endregion ISharedRegionModule | 141 | #endregion ISharedRegionModule |
142 | 142 | ||
143 | private void MakeStat(string pName, string pUnit, string pContainer, Action<Stat> act) | 143 | private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act) |
144 | { | 144 | { |
145 | Stat stat = new Stat(pName, pName, "", pUnit, CategoryServer, pContainer, StatType.Pull, act, StatVerbosity.Info); | 145 | string desc = pDesc; |
146 | if (desc == null) | ||
147 | desc = pName; | ||
148 | Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, act, StatVerbosity.Info); | ||
146 | StatsManager.RegisterStat(stat); | 149 | StatsManager.RegisterStat(stat); |
147 | RegisteredStats.Add(pName, stat); | 150 | RegisteredStats.Add(pName, stat); |
148 | } | 151 | } |
@@ -166,16 +169,16 @@ public class ServerStats : ISharedRegionModule | |||
166 | StatsManager.RegisterStat(tempStat); | 169 | StatsManager.RegisterStat(tempStat); |
167 | RegisteredStats.Add(tempName, tempStat); | 170 | RegisteredStats.Add(tempName, tempStat); |
168 | 171 | ||
169 | MakeStat("TotalProcessorTime", "sec", ContainerProcessor, | 172 | MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor, |
170 | (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; }); | 173 | (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; }); |
171 | 174 | ||
172 | MakeStat("UserProcessorTime", "sec", ContainerProcessor, | 175 | MakeStat("UserProcessorTime", null, "sec", ContainerProcessor, |
173 | (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; }); | 176 | (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; }); |
174 | 177 | ||
175 | MakeStat("PrivilegedProcessorTime", "sec", ContainerProcessor, | 178 | MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor, |
176 | (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; }); | 179 | (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; }); |
177 | 180 | ||
178 | MakeStat("Threads", "threads", ContainerProcessor, | 181 | MakeStat("Threads", null, "threads", ContainerProcessor, |
179 | (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; }); | 182 | (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; }); |
180 | } | 183 | } |
181 | catch (Exception e) | 184 | catch (Exception e) |
@@ -196,8 +199,10 @@ public class ServerStats : ISharedRegionModule | |||
196 | string nicInterfaceType = nic.NetworkInterfaceType.ToString(); | 199 | string nicInterfaceType = nic.NetworkInterfaceType.ToString(); |
197 | if (!okInterfaceTypes.Contains(nicInterfaceType)) | 200 | if (!okInterfaceTypes.Contains(nicInterfaceType)) |
198 | { | 201 | { |
199 | m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'. To include, add to [Monitoring]NetworkInterfaceTypes='Ethernet,Loopback'", | 202 | m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.", |
200 | LogHeader, nic.Name, nicInterfaceType); | 203 | LogHeader, nic.Name, nicInterfaceType); |
204 | m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}", | ||
205 | LogHeader, NetworkInterfaceTypes); | ||
201 | continue; | 206 | continue; |
202 | } | 207 | } |
203 | 208 | ||
@@ -206,14 +211,15 @@ public class ServerStats : ISharedRegionModule | |||
206 | IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics(); | 211 | IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics(); |
207 | if (nicStats != null) | 212 | if (nicStats != null) |
208 | { | 213 | { |
209 | MakeStat("BytesRcvd/" + nic.Name, "KB", ContainerNetwork, | 214 | MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork, |
210 | (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); }); | 215 | (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); }); |
211 | MakeStat("BytesSent/" + nic.Name, "KB", ContainerNetwork, | 216 | MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork, |
212 | (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); }); | 217 | (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); }); |
213 | MakeStat("TotalBytes/" + nic.Name, "KB", ContainerNetwork, | 218 | MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork, |
214 | (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); }); | 219 | (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); }); |
215 | } | 220 | } |
216 | } | 221 | } |
222 | // TODO: add IPv6 (it may actually happen someday) | ||
217 | } | 223 | } |
218 | } | 224 | } |
219 | catch (Exception e) | 225 | catch (Exception e) |
@@ -221,13 +227,13 @@ public class ServerStats : ISharedRegionModule | |||
221 | m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e); | 227 | m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e); |
222 | } | 228 | } |
223 | 229 | ||
224 | MakeStat("ProcessMemory", "MB", ContainerMemory, | 230 | MakeStat("ProcessMemory", null, "MB", ContainerMemory, |
225 | (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; }); | 231 | (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; }); |
226 | MakeStat("ObjectMemory", "MB", ContainerMemory, | 232 | MakeStat("ObjectMemory", null, "MB", ContainerMemory, |
227 | (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; }); | 233 | (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; }); |
228 | MakeStat("LastMemoryChurn", "MB/sec", ContainerMemory, | 234 | MakeStat("LastMemoryChurn", null, "MB/sec", ContainerMemory, |
229 | (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); }); | 235 | (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); }); |
230 | MakeStat("AverageMemoryChurn", "MB/sec", ContainerMemory, | 236 | MakeStat("AverageMemoryChurn", null, "MB/sec", ContainerMemory, |
231 | (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); }); | 237 | (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); }); |
232 | } | 238 | } |
233 | 239 | ||
@@ -263,6 +269,8 @@ public class ServerStats : ISharedRegionModule | |||
263 | } | 269 | } |
264 | } | 270 | } |
265 | 271 | ||
272 | // Lookup the nic that goes with this stat and set the value by using a fetch action. | ||
273 | // Not sure about closure with delegates inside delegates. | ||
266 | private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat); | 274 | private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat); |
267 | private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor) | 275 | private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor) |
268 | { | 276 | { |
@@ -275,7 +283,10 @@ public class ServerStats : ISharedRegionModule | |||
275 | { | 283 | { |
276 | IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics(); | 284 | IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics(); |
277 | if (intrStats != null) | 285 | if (intrStats != null) |
278 | stat.Value = Math.Round(getter(intrStats) / factor, 3); | 286 | { |
287 | double newVal = Math.Round(getter(intrStats) / factor, 3); | ||
288 | stat.Value = newVal; | ||
289 | } | ||
279 | break; | 290 | break; |
280 | } | 291 | } |
281 | } | 292 | } |
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs index 40adba1..e498c6a 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs | |||
@@ -145,7 +145,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
145 | /// | 145 | /// |
146 | /// </summary> | 146 | /// </summary> |
147 | // ----------------------------------------------------------------- | 147 | // ----------------------------------------------------------------- |
148 | public JsonStoreNodeType PathType(string expr) | 148 | public JsonStoreNodeType GetNodeType(string expr) |
149 | { | 149 | { |
150 | Stack<string> path; | 150 | Stack<string> path; |
151 | if (! ParsePathExpression(expr,out path)) | 151 | if (! ParsePathExpression(expr,out path)) |
@@ -173,6 +173,43 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
173 | /// | 173 | /// |
174 | /// </summary> | 174 | /// </summary> |
175 | // ----------------------------------------------------------------- | 175 | // ----------------------------------------------------------------- |
176 | public JsonStoreValueType GetValueType(string expr) | ||
177 | { | ||
178 | Stack<string> path; | ||
179 | if (! ParsePathExpression(expr,out path)) | ||
180 | return JsonStoreValueType.Undefined; | ||
181 | |||
182 | OSD result = ProcessPathExpression(ValueStore,path); | ||
183 | |||
184 | if (result == null) | ||
185 | return JsonStoreValueType.Undefined; | ||
186 | |||
187 | if (result is OSDMap) | ||
188 | return JsonStoreValueType.Undefined; | ||
189 | |||
190 | if (result is OSDArray) | ||
191 | return JsonStoreValueType.Undefined; | ||
192 | |||
193 | if (result is OSDBoolean) | ||
194 | return JsonStoreValueType.Boolean; | ||
195 | |||
196 | if (result is OSDInteger) | ||
197 | return JsonStoreValueType.Integer; | ||
198 | |||
199 | if (result is OSDReal) | ||
200 | return JsonStoreValueType.Float; | ||
201 | |||
202 | if (result is OSDString) | ||
203 | return JsonStoreValueType.String; | ||
204 | |||
205 | return JsonStoreValueType.Undefined; | ||
206 | } | ||
207 | |||
208 | // ----------------------------------------------------------------- | ||
209 | /// <summary> | ||
210 | /// | ||
211 | /// </summary> | ||
212 | // ----------------------------------------------------------------- | ||
176 | public int ArrayLength(string expr) | 213 | public int ArrayLength(string expr) |
177 | { | 214 | { |
178 | Stack<string> path; | 215 | Stack<string> path; |
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs index e78a2f4..5fbfcc5 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs | |||
@@ -270,7 +270,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
270 | /// | 270 | /// |
271 | /// </summary> | 271 | /// </summary> |
272 | // ----------------------------------------------------------------- | 272 | // ----------------------------------------------------------------- |
273 | public JsonStoreNodeType GetPathType(UUID storeID, string path) | 273 | public JsonStoreNodeType GetNodeType(UUID storeID, string path) |
274 | { | 274 | { |
275 | if (! m_enabled) return JsonStoreNodeType.Undefined; | 275 | if (! m_enabled) return JsonStoreNodeType.Undefined; |
276 | 276 | ||
@@ -287,7 +287,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
287 | try | 287 | try |
288 | { | 288 | { |
289 | lock (map) | 289 | lock (map) |
290 | return map.PathType(path); | 290 | return map.GetNodeType(path); |
291 | } | 291 | } |
292 | catch (Exception e) | 292 | catch (Exception e) |
293 | { | 293 | { |
@@ -302,6 +302,38 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
302 | /// | 302 | /// |
303 | /// </summary> | 303 | /// </summary> |
304 | // ----------------------------------------------------------------- | 304 | // ----------------------------------------------------------------- |
305 | public JsonStoreValueType GetValueType(UUID storeID, string path) | ||
306 | { | ||
307 | if (! m_enabled) return JsonStoreValueType.Undefined; | ||
308 | |||
309 | JsonStore map = null; | ||
310 | lock (m_JsonValueStore) | ||
311 | { | ||
312 | if (! m_JsonValueStore.TryGetValue(storeID,out map)) | ||
313 | { | ||
314 | m_log.InfoFormat("[JsonStore] Missing store {0}",storeID); | ||
315 | return JsonStoreValueType.Undefined; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | try | ||
320 | { | ||
321 | lock (map) | ||
322 | return map.GetValueType(path); | ||
323 | } | ||
324 | catch (Exception e) | ||
325 | { | ||
326 | m_log.Error(string.Format("[JsonStore]: Path test failed for {0} in {1}", path, storeID), e); | ||
327 | } | ||
328 | |||
329 | return JsonStoreValueType.Undefined; | ||
330 | } | ||
331 | |||
332 | // ----------------------------------------------------------------- | ||
333 | /// <summary> | ||
334 | /// | ||
335 | /// </summary> | ||
336 | // ----------------------------------------------------------------- | ||
305 | public bool SetValue(UUID storeID, string path, string value, bool useJson) | 337 | public bool SetValue(UUID storeID, string path, string value, bool useJson) |
306 | { | 338 | { |
307 | if (! m_enabled) return false; | 339 | if (! m_enabled) return false; |
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs index e13eb56..4a754a9 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs | |||
@@ -192,16 +192,32 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
192 | #region ScriptConstantsInterface | 192 | #region ScriptConstantsInterface |
193 | 193 | ||
194 | [ScriptConstant] | 194 | [ScriptConstant] |
195 | public static readonly int JSON_TYPE_UNDEF = (int)JsonStoreNodeType.Undefined; | 195 | public static readonly int JSON_NODETYPE_UNDEF = (int)JsonStoreNodeType.Undefined; |
196 | 196 | ||
197 | [ScriptConstant] | 197 | [ScriptConstant] |
198 | public static readonly int JSON_TYPE_OBJECT = (int)JsonStoreNodeType.Object; | 198 | public static readonly int JSON_NODETYPE_OBJECT = (int)JsonStoreNodeType.Object; |
199 | 199 | ||
200 | [ScriptConstant] | 200 | [ScriptConstant] |
201 | public static readonly int JSON_TYPE_ARRAY = (int)JsonStoreNodeType.Array; | 201 | public static readonly int JSON_NODETYPE_ARRAY = (int)JsonStoreNodeType.Array; |
202 | 202 | ||
203 | [ScriptConstant] | 203 | [ScriptConstant] |
204 | public static readonly int JSON_TYPE_VALUE = (int)JsonStoreNodeType.Value; | 204 | public static readonly int JSON_NODETYPE_VALUE = (int)JsonStoreNodeType.Value; |
205 | |||
206 | [ScriptConstant] | ||
207 | public static readonly int JSON_VALUETYPE_UNDEF = (int)JsonStoreValueType.Undefined; | ||
208 | |||
209 | [ScriptConstant] | ||
210 | public static readonly int JSON_VALUETYPE_BOOLEAN = (int)JsonStoreValueType.Boolean; | ||
211 | |||
212 | [ScriptConstant] | ||
213 | public static readonly int JSON_VALUETYPE_INTEGER = (int)JsonStoreValueType.Integer; | ||
214 | |||
215 | [ScriptConstant] | ||
216 | public static readonly int JSON_VALUETYPE_FLOAT = (int)JsonStoreValueType.Float; | ||
217 | |||
218 | [ScriptConstant] | ||
219 | public static readonly int JSON_VALUETYPE_STRING = (int)JsonStoreValueType.String; | ||
220 | |||
205 | 221 | ||
206 | #endregion | 222 | #endregion |
207 | 223 | ||
@@ -310,9 +326,20 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore | |||
310 | /// </summary> | 326 | /// </summary> |
311 | // ----------------------------------------------------------------- | 327 | // ----------------------------------------------------------------- |
312 | [ScriptInvocation] | 328 | [ScriptInvocation] |
313 | public int JsonGetPathType(UUID hostID, UUID scriptID, UUID storeID, string path) | 329 | public int JsonGetNodeType(UUID hostID, UUID scriptID, UUID storeID, string path) |
330 | { | ||
331 | return (int)m_store.GetNodeType(storeID,path); | ||
332 | } | ||
333 | |||
334 | // ----------------------------------------------------------------- | ||
335 | /// <summary> | ||
336 | /// | ||
337 | /// </summary> | ||
338 | // ----------------------------------------------------------------- | ||
339 | [ScriptInvocation] | ||
340 | public int JsonGetValueType(UUID hostID, UUID scriptID, UUID storeID, string path) | ||
314 | { | 341 | { |
315 | return (int)m_store.GetPathType(storeID,path); | 342 | return (int)m_store.GetValueType(storeID,path); |
316 | } | 343 | } |
317 | 344 | ||
318 | // ----------------------------------------------------------------- | 345 | // ----------------------------------------------------------------- |
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs index b64dbd4..bfa9937 100644 --- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs +++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs | |||
@@ -158,8 +158,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests | |||
158 | 158 | ||
159 | Assert.That(dsrv, Is.EqualTo(1)); | 159 | Assert.That(dsrv, Is.EqualTo(1)); |
160 | 160 | ||
161 | int tprv = (int)InvokeOp("JsonGetPathType", storeId, "Hello"); | 161 | int tprv = (int)InvokeOp("JsonGetNodeType", storeId, "Hello"); |
162 | Assert.That(tprv, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); | 162 | Assert.That(tprv, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_UNDEF)); |
163 | } | 163 | } |
164 | 164 | ||
165 | [Test] | 165 | [Test] |
@@ -277,8 +277,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests | |||
277 | int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello"); | 277 | int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello"); |
278 | Assert.That(returnValue, Is.EqualTo(1)); | 278 | Assert.That(returnValue, Is.EqualTo(1)); |
279 | 279 | ||
280 | int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello"); | 280 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello"); |
281 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); | 281 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_UNDEF)); |
282 | 282 | ||
283 | string returnValue2 = (string)InvokeOp("JsonGetValue", storeId, "Hello"); | 283 | string returnValue2 = (string)InvokeOp("JsonGetValue", storeId, "Hello"); |
284 | Assert.That(returnValue2, Is.EqualTo("")); | 284 | Assert.That(returnValue2, Is.EqualTo("")); |
@@ -291,8 +291,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests | |||
291 | int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello"); | 291 | int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello"); |
292 | Assert.That(returnValue, Is.EqualTo(1)); | 292 | Assert.That(returnValue, Is.EqualTo(1)); |
293 | 293 | ||
294 | int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello"); | 294 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello"); |
295 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); | 295 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_UNDEF)); |
296 | 296 | ||
297 | string returnValue2 = (string)InvokeOp("JsonGetJson", storeId, "Hello"); | 297 | string returnValue2 = (string)InvokeOp("JsonGetJson", storeId, "Hello"); |
298 | Assert.That(returnValue2, Is.EqualTo("")); | 298 | Assert.That(returnValue2, Is.EqualTo("")); |
@@ -306,11 +306,11 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests | |||
306 | int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello[0]"); | 306 | int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello[0]"); |
307 | Assert.That(returnValue, Is.EqualTo(1)); | 307 | Assert.That(returnValue, Is.EqualTo(1)); |
308 | 308 | ||
309 | int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello[0]"); | 309 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello[0]"); |
310 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_VALUE)); | 310 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_VALUE)); |
311 | 311 | ||
312 | result = (int)InvokeOp("JsonGetPathType", storeId, "Hello[1]"); | 312 | result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello[1]"); |
313 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); | 313 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_UNDEF)); |
314 | 314 | ||
315 | string stringReturnValue = (string)InvokeOp("JsonGetValue", storeId, "Hello[0]"); | 315 | string stringReturnValue = (string)InvokeOp("JsonGetValue", storeId, "Hello[0]"); |
316 | Assert.That(stringReturnValue, Is.EqualTo("value2")); | 316 | Assert.That(stringReturnValue, Is.EqualTo("value2")); |
@@ -433,7 +433,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests | |||
433 | } | 433 | } |
434 | 434 | ||
435 | [Test] | 435 | [Test] |
436 | public void TestJsonGetPathType() | 436 | public void TestJsonGetNodeType() |
437 | { | 437 | { |
438 | TestHelpers.InMethod(); | 438 | TestHelpers.InMethod(); |
439 | // TestHelpers.EnableLogging(); | 439 | // TestHelpers.EnableLogging(); |
@@ -441,41 +441,41 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests | |||
441 | UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ 'Hello' : { 'World' : [ 'one', 2 ] } }"); | 441 | UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ 'Hello' : { 'World' : [ 'one', 2 ] } }"); |
442 | 442 | ||
443 | { | 443 | { |
444 | int result = (int)InvokeOp("JsonGetPathType", storeId, "."); | 444 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "."); |
445 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_OBJECT)); | 445 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_OBJECT)); |
446 | } | 446 | } |
447 | 447 | ||
448 | { | 448 | { |
449 | int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello"); | 449 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello"); |
450 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_OBJECT)); | 450 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_OBJECT)); |
451 | } | 451 | } |
452 | 452 | ||
453 | { | 453 | { |
454 | int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello.World"); | 454 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello.World"); |
455 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_ARRAY)); | 455 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_ARRAY)); |
456 | } | 456 | } |
457 | 457 | ||
458 | { | 458 | { |
459 | int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello.World[0]"); | 459 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello.World[0]"); |
460 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_VALUE)); | 460 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_VALUE)); |
461 | } | 461 | } |
462 | 462 | ||
463 | { | 463 | { |
464 | int result = (int)InvokeOp("JsonGetPathType", storeId, "Hello.World[1]"); | 464 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "Hello.World[1]"); |
465 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_VALUE)); | 465 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_VALUE)); |
466 | } | 466 | } |
467 | 467 | ||
468 | // Test for non-existant path | 468 | // Test for non-existant path |
469 | { | 469 | { |
470 | int result = (int)InvokeOp("JsonGetPathType", storeId, "foo"); | 470 | int result = (int)InvokeOp("JsonGetNodeType", storeId, "foo"); |
471 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); | 471 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_UNDEF)); |
472 | } | 472 | } |
473 | 473 | ||
474 | // Test for non-existant store | 474 | // Test for non-existant store |
475 | { | 475 | { |
476 | UUID fakeStoreId = TestHelpers.ParseTail(0x500); | 476 | UUID fakeStoreId = TestHelpers.ParseTail(0x500); |
477 | int result = (int)InvokeOp("JsonGetPathType", fakeStoreId, "."); | 477 | int result = (int)InvokeOp("JsonGetNodeType", fakeStoreId, "."); |
478 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_TYPE_UNDEF)); | 478 | Assert.That(result, Is.EqualTo(JsonStoreScriptModule.JSON_NODETYPE_UNDEF)); |
479 | } | 479 | } |
480 | } | 480 | } |
481 | 481 | ||
diff --git a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs index c4b9117..0816b7b 100644 --- a/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs +++ b/OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs | |||
@@ -102,6 +102,8 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin | |||
102 | 102 | ||
103 | public override float Simulate(float timeStep) | 103 | public override float Simulate(float timeStep) |
104 | { | 104 | { |
105 | // Console.WriteLine("Simulating"); | ||
106 | |||
105 | float fps = 0; | 107 | float fps = 0; |
106 | for (int i = 0; i < _actors.Count; ++i) | 108 | for (int i = 0; i < _actors.Count; ++i) |
107 | { | 109 | { |
@@ -109,8 +111,11 @@ namespace OpenSim.Region.Physics.BasicPhysicsPlugin | |||
109 | Vector3 actorPosition = actor.Position; | 111 | Vector3 actorPosition = actor.Position; |
110 | Vector3 actorVelocity = actor.Velocity; | 112 | Vector3 actorVelocity = actor.Velocity; |
111 | 113 | ||
112 | actorPosition.X += actor.Velocity.X*timeStep; | 114 | // Console.WriteLine( |
113 | actorPosition.Y += actor.Velocity.Y*timeStep; | 115 | // "Processing actor {0}, starting pos {1}, starting vel {2}", i, actorPosition, actorVelocity); |
116 | |||
117 | actorPosition.X += actor.Velocity.X * timeStep; | ||
118 | actorPosition.Y += actor.Velocity.Y * timeStep; | ||
114 | 119 | ||
115 | if (actor.Position.Y < 0) | 120 | if (actor.Position.Y < 0) |
116 | { | 121 | { |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index f442ca2..e208d3a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | |||
@@ -205,7 +205,7 @@ public sealed class BSCharacter : BSPhysObject | |||
205 | // errors can creap in and the avatar will slowly float off in some direction. | 205 | // errors can creap in and the avatar will slowly float off in some direction. |
206 | // So, the problem is that, when an avatar is standing, we cannot tell creaping error | 206 | // So, the problem is that, when an avatar is standing, we cannot tell creaping error |
207 | // from real pushing. | 207 | // from real pushing. |
208 | // The code below keeps setting the velocity to zero hoping the world will keep pushing. | 208 | // The code below uses whether the collider is static or moving to decide whether to zero motion. |
209 | 209 | ||
210 | _velocityMotor.Step(timeStep); | 210 | _velocityMotor.Step(timeStep); |
211 | 211 | ||
@@ -244,6 +244,7 @@ public sealed class BSCharacter : BSPhysObject | |||
244 | } | 244 | } |
245 | else | 245 | else |
246 | { | 246 | { |
247 | // Supposed to be moving. | ||
247 | OMV.Vector3 stepVelocity = _velocityMotor.CurrentValue; | 248 | OMV.Vector3 stepVelocity = _velocityMotor.CurrentValue; |
248 | 249 | ||
249 | if (Friction != BSParam.AvatarFriction) | 250 | if (Friction != BSParam.AvatarFriction) |
@@ -276,8 +277,8 @@ public sealed class BSCharacter : BSPhysObject | |||
276 | }); | 277 | }); |
277 | } | 278 | } |
278 | 279 | ||
279 | // Decide of the character is colliding with a low object and compute a force to pop the | 280 | // Decide if the character is colliding with a low object and compute a force to pop the |
280 | // avatar up so it has a chance of walking up and over the low object. | 281 | // avatar up so it can walk up and over the low objects. |
281 | private OMV.Vector3 WalkUpStairs() | 282 | private OMV.Vector3 WalkUpStairs() |
282 | { | 283 | { |
283 | OMV.Vector3 ret = OMV.Vector3.Zero; | 284 | OMV.Vector3 ret = OMV.Vector3.Zero; |
@@ -476,17 +477,19 @@ public sealed class BSCharacter : BSPhysObject | |||
476 | if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) | 477 | if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) |
477 | { | 478 | { |
478 | // The character is out of the known/simulated area. | 479 | // The character is out of the known/simulated area. |
479 | // Upper levels of code will handle the transition to other areas so, for | 480 | // Force the avatar position to be within known. ScenePresence will use the position |
480 | // the time, we just ignore the position. | 481 | // plus the velocity to decide if the avatar is moving out of the region. |
481 | return ret; | 482 | RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); |
483 | DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); | ||
484 | return true; | ||
482 | } | 485 | } |
483 | 486 | ||
484 | // If below the ground, move the avatar up | 487 | // If below the ground, move the avatar up |
485 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); | 488 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); |
486 | if (Position.Z < terrainHeight) | 489 | if (Position.Z < terrainHeight) |
487 | { | 490 | { |
488 | DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); | 491 | DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, _position, terrainHeight); |
489 | _position.Z = terrainHeight + 2.0f; | 492 | _position.Z = terrainHeight + BSParam.AvatarBelowGroundUpCorrectionMeters; |
490 | ret = true; | 493 | ret = true; |
491 | } | 494 | } |
492 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) | 495 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) |
@@ -806,14 +809,7 @@ public sealed class BSCharacter : BSPhysObject | |||
806 | private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | 809 | private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { |
807 | if (force.IsFinite()) | 810 | if (force.IsFinite()) |
808 | { | 811 | { |
809 | float magnitude = force.Length(); | 812 | OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); |
810 | if (magnitude > BSParam.MaxAddForceMagnitude) | ||
811 | { | ||
812 | // Force has a limit | ||
813 | force = force / magnitude * BSParam.MaxAddForceMagnitude; | ||
814 | } | ||
815 | |||
816 | OMV.Vector3 addForce = force; | ||
817 | // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); | 813 | // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); |
818 | 814 | ||
819 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() | 815 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() |
@@ -902,6 +898,7 @@ public sealed class BSCharacter : BSPhysObject | |||
902 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. | 898 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. |
903 | if (PositionSanityCheck(true)) | 899 | if (PositionSanityCheck(true)) |
904 | { | 900 | { |
901 | DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, _position); | ||
905 | entprop.Position = _position; | 902 | entprop.Position = _position; |
906 | } | 903 | } |
907 | 904 | ||
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 235cefc..38596fa 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | |||
@@ -143,7 +143,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
143 | { | 143 | { |
144 | enableAngularVerticalAttraction = true; | 144 | enableAngularVerticalAttraction = true; |
145 | enableAngularDeflection = false; | 145 | enableAngularDeflection = false; |
146 | enableAngularBanking = false; | 146 | enableAngularBanking = true; |
147 | if (BSParam.VehicleDebuggingEnabled) | 147 | if (BSParam.VehicleDebuggingEnabled) |
148 | { | 148 | { |
149 | enableAngularVerticalAttraction = true; | 149 | enableAngularVerticalAttraction = true; |
@@ -1280,11 +1280,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1280 | // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement | 1280 | // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement |
1281 | // TODO: This is here because this is where ODE put it but documentation says it | 1281 | // TODO: This is here because this is where ODE put it but documentation says it |
1282 | // is a linear effect. Where should this check go? | 1282 | // is a linear effect. Where should this check go? |
1283 | if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) | 1283 | //if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) |
1284 | { | 1284 | // { |
1285 | angularMotorContributionV.X = 0f; | 1285 | // angularMotorContributionV.X = 0f; |
1286 | angularMotorContributionV.Y = 0f; | 1286 | // angularMotorContributionV.Y = 0f; |
1287 | } | 1287 | // } |
1288 | 1288 | ||
1289 | VehicleRotationalVelocity += angularMotorContributionV * VehicleOrientation; | 1289 | VehicleRotationalVelocity += angularMotorContributionV * VehicleOrientation; |
1290 | VDetailLog("{0}, MoveAngular,angularTurning,angularMotorContrib={1}", Prim.LocalID, angularMotorContributionV); | 1290 | VDetailLog("{0}, MoveAngular,angularTurning,angularMotorContrib={1}", Prim.LocalID, angularMotorContributionV); |
@@ -1335,7 +1335,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1335 | Vector3 unscaledContribVerticalErrorV = vertContributionV; // DEBUG DEBUG | 1335 | Vector3 unscaledContribVerticalErrorV = vertContributionV; // DEBUG DEBUG |
1336 | vertContributionV /= m_verticalAttractionTimescale; | 1336 | vertContributionV /= m_verticalAttractionTimescale; |
1337 | 1337 | ||
1338 | VehicleRotationalVelocity += vertContributionV * VehicleOrientation; | 1338 | VehicleRotationalVelocity += vertContributionV; |
1339 | 1339 | ||
1340 | VDetailLog("{0}, MoveAngular,verticalAttraction,,origRotVW={1},vertError={2},unscaledV={3},eff={4},ts={5},vertContribV={6}", | 1340 | VDetailLog("{0}, MoveAngular,verticalAttraction,,origRotVW={1},vertError={2},unscaledV={3},eff={4},ts={5},vertContribV={6}", |
1341 | Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, | 1341 | Prim.LocalID, origRotVelW, verticalError, unscaledContribVerticalErrorV, |
@@ -1437,24 +1437,25 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1437 | // As the vehicle rolls to the right or left, the Y value will increase from | 1437 | // As the vehicle rolls to the right or left, the Y value will increase from |
1438 | // zero (straight up) to 1 or -1 (full tilt right or left) | 1438 | // zero (straight up) to 1 or -1 (full tilt right or left) |
1439 | Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation; | 1439 | Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation; |
1440 | |||
1441 | // Figure out the yaw value for this much roll. | ||
1442 | // Squared because that seems to give a good value | ||
1443 | float yawAngle = (float)Math.Asin(rollComponents.Y * rollComponents.Y) * m_bankingEfficiency; | ||
1444 | 1440 | ||
1441 | // Figure out the yaw value for this much roll. | ||
1442 | float yawAngle = m_angularMotorDirection.X * m_bankingEfficiency; | ||
1445 | // actual error = static turn error + dynamic turn error | 1443 | // actual error = static turn error + dynamic turn error |
1446 | float mixedYawAngle = yawAngle * (1f - m_bankingMix) + yawAngle * m_bankingMix * VehicleForwardSpeed; | 1444 | float mixedYawAngle =(yawAngle * (1f - m_bankingMix)) + ((yawAngle * m_bankingMix) * VehicleForwardSpeed); |
1447 | 1445 | ||
1448 | // TODO: the banking effect should not go to infinity but what to limit it to? | 1446 | // TODO: the banking effect should not go to infinity but what to limit it to? |
1449 | mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f); | 1447 | // And what should happen when this is being added to a user defined yaw that is already PI*4? |
1448 | mixedYawAngle = ClampInRange(-12, mixedYawAngle, 12); | ||
1450 | 1449 | ||
1451 | // Build the force vector to change rotation from what it is to what it should be | 1450 | // Build the force vector to change rotation from what it is to what it should be |
1452 | bankingContributionV.Z = -mixedYawAngle; | 1451 | bankingContributionV.Z = -mixedYawAngle; |
1453 | 1452 | ||
1454 | // Don't do it all at once. | 1453 | // Don't do it all at once. Fudge because 1 second is too fast with most user defined roll as PI*4. |
1455 | bankingContributionV /= m_bankingTimescale; | 1454 | bankingContributionV /= m_bankingTimescale * BSParam.VehicleAngularBankingTimescaleFudge; |
1456 | 1455 | ||
1457 | VehicleRotationalVelocity += bankingContributionV * VehicleOrientation; | 1456 | //VehicleRotationalVelocity += bankingContributionV * VehicleOrientation; |
1457 | VehicleRotationalVelocity += bankingContributionV; | ||
1458 | |||
1458 | 1459 | ||
1459 | VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", | 1460 | VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", |
1460 | Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV); | 1461 | Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV); |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index fa58109..77bdacb 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | |||
@@ -107,6 +107,7 @@ public static class BSParam | |||
107 | public static float AvatarCapsuleDepth { get; private set; } | 107 | public static float AvatarCapsuleDepth { get; private set; } |
108 | public static float AvatarCapsuleHeight { get; private set; } | 108 | public static float AvatarCapsuleHeight { get; private set; } |
109 | public static float AvatarContactProcessingThreshold { get; private set; } | 109 | public static float AvatarContactProcessingThreshold { get; private set; } |
110 | public static float AvatarBelowGroundUpCorrectionMeters { get; private set; } | ||
110 | public static float AvatarStepHeight { get; private set; } | 111 | public static float AvatarStepHeight { get; private set; } |
111 | public static float AvatarStepApproachFactor { get; private set; } | 112 | public static float AvatarStepApproachFactor { get; private set; } |
112 | public static float AvatarStepForceFactor { get; private set; } | 113 | public static float AvatarStepForceFactor { get; private set; } |
@@ -122,6 +123,7 @@ public static class BSParam | |||
122 | public static Vector3 VehicleLinearFactor { get; private set; } | 123 | public static Vector3 VehicleLinearFactor { get; private set; } |
123 | public static Vector3 VehicleAngularFactor { get; private set; } | 124 | public static Vector3 VehicleAngularFactor { get; private set; } |
124 | public static float VehicleGroundGravityFudge { get; private set; } | 125 | public static float VehicleGroundGravityFudge { get; private set; } |
126 | public static float VehicleAngularBankingTimescaleFudge { get; private set; } | ||
125 | public static bool VehicleDebuggingEnabled { get; private set; } | 127 | public static bool VehicleDebuggingEnabled { get; private set; } |
126 | 128 | ||
127 | // Linkset implementation parameters | 129 | // Linkset implementation parameters |
@@ -497,6 +499,10 @@ public static class BSParam | |||
497 | 0.1f, | 499 | 0.1f, |
498 | (s) => { return AvatarContactProcessingThreshold; }, | 500 | (s) => { return AvatarContactProcessingThreshold; }, |
499 | (s,v) => { AvatarContactProcessingThreshold = v; } ), | 501 | (s,v) => { AvatarContactProcessingThreshold = v; } ), |
502 | new ParameterDefn<float>("AvatarBelowGroundUpCorrectionMeters", "Meters to move avatar up if it seems to be below ground", | ||
503 | 1.0f, | ||
504 | (s) => { return AvatarBelowGroundUpCorrectionMeters; }, | ||
505 | (s,v) => { AvatarBelowGroundUpCorrectionMeters = v; } ), | ||
500 | new ParameterDefn<float>("AvatarStepHeight", "Height of a step obstacle to consider step correction", | 506 | new ParameterDefn<float>("AvatarStepHeight", "Height of a step obstacle to consider step correction", |
501 | 0.3f, | 507 | 0.3f, |
502 | (s) => { return AvatarStepHeight; }, | 508 | (s) => { return AvatarStepHeight; }, |
@@ -538,10 +544,14 @@ public static class BSParam | |||
538 | 0.0f, | 544 | 0.0f, |
539 | (s) => { return VehicleRestitution; }, | 545 | (s) => { return VehicleRestitution; }, |
540 | (s,v) => { VehicleRestitution = v; } ), | 546 | (s,v) => { VehicleRestitution = v; } ), |
541 | new ParameterDefn<float>("VehicleGroundGravityFudge", "Factor to multiple gravity if a ground vehicle is probably on the ground (0.0 - 1.0)", | 547 | new ParameterDefn<float>("VehicleGroundGravityFudge", "Factor to multiply gravity if a ground vehicle is probably on the ground (0.0 - 1.0)", |
542 | 0.2f, | 548 | 0.2f, |
543 | (s) => { return VehicleGroundGravityFudge; }, | 549 | (s) => { return VehicleGroundGravityFudge; }, |
544 | (s,v) => { VehicleGroundGravityFudge = v; } ), | 550 | (s,v) => { VehicleGroundGravityFudge = v; } ), |
551 | new ParameterDefn<float>("VehicleAngularBankingTimescaleFudge", "Factor to multiple angular banking timescale. Tune to increase realism.", | ||
552 | 60.0f, | ||
553 | (s) => { return VehicleAngularBankingTimescaleFudge; }, | ||
554 | (s,v) => { VehicleAngularBankingTimescaleFudge = v; } ), | ||
545 | new ParameterDefn<bool>("VehicleDebuggingEnable", "Turn on/off vehicle debugging", | 555 | new ParameterDefn<bool>("VehicleDebuggingEnable", "Turn on/off vehicle debugging", |
546 | false, | 556 | false, |
547 | (s) => { return VehicleDebuggingEnabled; }, | 557 | (s) => { return VehicleDebuggingEnabled; }, |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 2e9db39..e8040d8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs | |||
@@ -337,6 +337,54 @@ public sealed class BSTerrainManager : IDisposable | |||
337 | return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ); | 337 | return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ); |
338 | } | 338 | } |
339 | 339 | ||
340 | // Return a new position that is over known terrain if the position is outside our terrain. | ||
341 | public Vector3 ClampPositionIntoKnownTerrain(Vector3 pPos) | ||
342 | { | ||
343 | Vector3 ret = pPos; | ||
344 | |||
345 | // Can't do this function if we don't know about any terrain. | ||
346 | if (m_terrains.Count == 0) | ||
347 | return ret; | ||
348 | |||
349 | int loopPrevention = 5; | ||
350 | Vector3 terrainBaseXYZ; | ||
351 | BSTerrainPhys physTerrain; | ||
352 | while (!GetTerrainPhysicalAtXYZ(ret, out physTerrain, out terrainBaseXYZ)) | ||
353 | { | ||
354 | // The passed position is not within a known terrain area. | ||
355 | |||
356 | // First, base addresses are never negative so correct for that possible problem. | ||
357 | if (ret.X < 0f || ret.Y < 0f) | ||
358 | { | ||
359 | if (ret.X < 0f) | ||
360 | ret.X = 0f; | ||
361 | if (ret.Y < 0f) | ||
362 | ret.Y = 0f; | ||
363 | DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}", | ||
364 | BSScene.DetailLogZero, pPos, ret); | ||
365 | } | ||
366 | else | ||
367 | { | ||
368 | // Must be off the top of a region. Find an adjacent region to move into. | ||
369 | Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ); | ||
370 | |||
371 | ret.X = Math.Min(ret.X, adjacentTerrainBase.X + DefaultRegionSize.X); | ||
372 | ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + DefaultRegionSize.Y); | ||
373 | DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}", | ||
374 | BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret); | ||
375 | } | ||
376 | if (loopPrevention-- < 0f) | ||
377 | { | ||
378 | // The 'while' is a little dangerous so this prevents looping forever if the | ||
379 | // mapping of the terrains ever gets messed up (like nothing at <0,0>) or | ||
380 | // the list of terrains is in transition. | ||
381 | DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,suppressingFindAdjacentRegionLoop", BSScene.DetailLogZero); | ||
382 | break; | ||
383 | } | ||
384 | } | ||
385 | return ret; | ||
386 | } | ||
387 | |||
340 | // Given an X and Y, find the height of the terrain. | 388 | // Given an X and Y, find the height of the terrain. |
341 | // Since we could be handling multiple terrains for a mega-region, | 389 | // Since we could be handling multiple terrains for a mega-region, |
342 | // the base of the region is calcuated assuming all regions are | 390 | // the base of the region is calcuated assuming all regions are |
@@ -400,18 +448,60 @@ public sealed class BSTerrainManager : IDisposable | |||
400 | // the descriptor class and the 'base' fo the addresses therein. | 448 | // the descriptor class and the 'base' fo the addresses therein. |
401 | private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase) | 449 | private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase) |
402 | { | 450 | { |
403 | int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; | 451 | bool ret = false; |
404 | int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; | 452 | |
405 | Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f); | 453 | Vector3 terrainBaseXYZ = Vector3.Zero; |
454 | if (pos.X < 0f || pos.Y < 0f) | ||
455 | { | ||
456 | // We don't handle negative addresses so just make up a base that will not be found. | ||
457 | terrainBaseXYZ = new Vector3(-DefaultRegionSize.X, -DefaultRegionSize.Y, 0f); | ||
458 | } | ||
459 | else | ||
460 | { | ||
461 | int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; | ||
462 | int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; | ||
463 | terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f); | ||
464 | } | ||
406 | 465 | ||
407 | BSTerrainPhys physTerrain = null; | 466 | BSTerrainPhys physTerrain = null; |
408 | lock (m_terrains) | 467 | lock (m_terrains) |
409 | { | 468 | { |
410 | m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain); | 469 | ret = m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain); |
411 | } | 470 | } |
412 | outTerrainBase = terrainBaseXYZ; | 471 | outTerrainBase = terrainBaseXYZ; |
413 | outPhysTerrain = physTerrain; | 472 | outPhysTerrain = physTerrain; |
414 | return (physTerrain != null); | 473 | return ret; |
474 | } | ||
475 | |||
476 | // Given a terrain base, return a terrain base for a terrain that is closer to <0,0> than | ||
477 | // this one. Usually used to return an out of bounds object to a known place. | ||
478 | private Vector3 FindAdjacentTerrainBase(Vector3 pTerrainBase) | ||
479 | { | ||
480 | Vector3 ret = pTerrainBase; | ||
481 | ret.Z = 0f; | ||
482 | lock (m_terrains) | ||
483 | { | ||
484 | // Once down to the <0,0> region, we have to be done. | ||
485 | while (ret.X > 0f && ret.Y > 0f) | ||
486 | { | ||
487 | if (ret.X > 0f) | ||
488 | { | ||
489 | ret.X = Math.Max(0f, ret.X - DefaultRegionSize.X); | ||
490 | DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingX,terrainBase={1}", BSScene.DetailLogZero, ret); | ||
491 | if (m_terrains.ContainsKey(ret)) | ||
492 | break; | ||
493 | } | ||
494 | if (ret.Y > 0f) | ||
495 | { | ||
496 | ret.Y = Math.Max(0f, ret.Y - DefaultRegionSize.Y); | ||
497 | DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingY,terrainBase={1}", BSScene.DetailLogZero, ret); | ||
498 | if (m_terrains.ContainsKey(ret)) | ||
499 | break; | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | |||
504 | return ret; | ||
415 | } | 505 | } |
416 | 506 | ||
417 | // Although no one seems to check this, I do support combining. | 507 | // Although no one seems to check this, I do support combining. |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index d7e800d..57a5ff2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs | |||
@@ -215,7 +215,8 @@ public sealed class BSTerrainMesh : BSTerrainPhys | |||
215 | 215 | ||
216 | float magX = (float)sizeX / extentX; | 216 | float magX = (float)sizeX / extentX; |
217 | float magY = (float)sizeY / extentY; | 217 | float magY = (float)sizeY / extentY; |
218 | physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}", | 218 | if (physicsScene != null) |
219 | physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}", | ||
219 | BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY); | 220 | BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY); |
220 | float minHeight = float.MaxValue; | 221 | float minHeight = float.MaxValue; |
221 | // Note that sizeX+1 vertices are created since there is land between this and the next region. | 222 | // Note that sizeX+1 vertices are created since there is land between this and the next region. |
@@ -257,7 +258,8 @@ public sealed class BSTerrainMesh : BSTerrainPhys | |||
257 | } | 258 | } |
258 | catch (Exception e) | 259 | catch (Exception e) |
259 | { | 260 | { |
260 | physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}", | 261 | if (physicsScene != null) |
262 | physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}", | ||
261 | LogHeader, physicsScene.RegionName, extentBase, e); | 263 | LogHeader, physicsScene.RegionName, extentBase, e); |
262 | } | 264 | } |
263 | 265 | ||
diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs index a133e51..b4abc1d 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs | |||
@@ -68,6 +68,11 @@ public class RegionCombinerLargeLandChannel : ILandChannel | |||
68 | RootRegionLandChannel.Clear(setupDefaultParcel); | 68 | RootRegionLandChannel.Clear(setupDefaultParcel); |
69 | } | 69 | } |
70 | 70 | ||
71 | public ILandObject GetLandObject(Vector3 position) | ||
72 | { | ||
73 | return GetLandObject(position.X, position.Y); | ||
74 | } | ||
75 | |||
71 | public ILandObject GetLandObject(int x, int y) | 76 | public ILandObject GetLandObject(int x, int y) |
72 | { | 77 | { |
73 | //m_log.DebugFormat("[BIGLANDTESTINT]: <{0},{1}>", x, y); | 78 | //m_log.DebugFormat("[BIGLANDTESTINT]: <{0},{1}>", x, y); |
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index cf6f13e..e1f0071 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | |||
@@ -283,6 +283,80 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
283 | } | 283 | } |
284 | } | 284 | } |
285 | 285 | ||
286 | /// <summary> | ||
287 | /// Get a given link entity from a linkset (linked objects and any sitting avatars). | ||
288 | /// </summary> | ||
289 | /// <remarks> | ||
290 | /// If there are any ScenePresence's in the linkset (i.e. because they are sat upon one of the prims), then | ||
291 | /// these are counted as extra entities that correspond to linknums beyond the number of prims in the linkset. | ||
292 | /// The ScenePresences receive linknums in the order in which they sat. | ||
293 | /// </remarks> | ||
294 | /// <returns> | ||
295 | /// The link entity. null if not found. | ||
296 | /// </returns> | ||
297 | /// <param name='linknum'> | ||
298 | /// Can be either a non-negative integer or ScriptBaseClass.LINK_THIS (-4). | ||
299 | /// If ScriptBaseClass.LINK_THIS then the entity containing the script is returned. | ||
300 | /// If the linkset has one entity and a linknum of zero is given, then the single entity is returned. If any | ||
301 | /// positive integer is given in this case then null is returned. | ||
302 | /// If the linkset has more than one entity and a linknum greater than zero but equal to or less than the number | ||
303 | /// of entities, then the entity which corresponds to that linknum is returned. | ||
304 | /// Otherwise, if a positive linknum is given which is greater than the number of entities in the linkset, then | ||
305 | /// null is returned. | ||
306 | /// </param> | ||
307 | public ISceneEntity GetLinkEntity(int linknum) | ||
308 | { | ||
309 | if (linknum < 0) | ||
310 | { | ||
311 | if (linknum == ScriptBaseClass.LINK_THIS) | ||
312 | return m_host; | ||
313 | else | ||
314 | return null; | ||
315 | } | ||
316 | |||
317 | int actualPrimCount = m_host.ParentGroup.PrimCount; | ||
318 | List<UUID> sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars(); | ||
319 | int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count; | ||
320 | |||
321 | // Special case for a single prim. In this case the linknum is zero. However, this will not match a single | ||
322 | // prim that has any avatars sat upon it (in which case the root prim is link 1). | ||
323 | if (linknum == 0) | ||
324 | { | ||
325 | if (actualPrimCount == 1 && sittingAvatarIds.Count == 0) | ||
326 | return m_host; | ||
327 | |||
328 | return null; | ||
329 | } | ||
330 | // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but | ||
331 | // here we must match 1 (ScriptBaseClass.LINK_ROOT). | ||
332 | else if (linknum == ScriptBaseClass.LINK_ROOT && actualPrimCount == 1) | ||
333 | { | ||
334 | if (sittingAvatarIds.Count > 0) | ||
335 | return m_host.ParentGroup.RootPart; | ||
336 | else | ||
337 | return null; | ||
338 | } | ||
339 | else if (linknum <= adjustedPrimCount) | ||
340 | { | ||
341 | if (linknum <= actualPrimCount) | ||
342 | { | ||
343 | return m_host.ParentGroup.GetLinkNumPart(linknum); | ||
344 | } | ||
345 | else | ||
346 | { | ||
347 | ScenePresence sp = World.GetScenePresence(sittingAvatarIds[linknum - actualPrimCount - 1]); | ||
348 | if (sp != null) | ||
349 | return sp; | ||
350 | else | ||
351 | return null; | ||
352 | } | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | return null; | ||
357 | } | ||
358 | } | ||
359 | |||
286 | public List<SceneObjectPart> GetLinkParts(int linkType) | 360 | public List<SceneObjectPart> GetLinkParts(int linkType) |
287 | { | 361 | { |
288 | return GetLinkParts(m_host, linkType); | 362 | return GetLinkParts(m_host, linkType); |
@@ -2174,23 +2248,25 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
2174 | if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0) | 2248 | if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0) |
2175 | q = avatar.CameraRotation; // Mouselook | 2249 | q = avatar.CameraRotation; // Mouselook |
2176 | else | 2250 | else |
2177 | q = avatar.Rotation; // Currently infrequently updated so may be inaccurate | 2251 | q = avatar.GetWorldRotation(); // Currently infrequently updated so may be inaccurate |
2178 | } | 2252 | } |
2179 | else | 2253 | else |
2180 | q = part.ParentGroup.GroupRotation; // Likely never get here but just in case | 2254 | q = part.ParentGroup.GroupRotation; // Likely never get here but just in case |
2181 | } | 2255 | } |
2182 | else | 2256 | else |
2183 | q = part.ParentGroup.GroupRotation; // just the group rotation | 2257 | q = part.ParentGroup.GroupRotation; // just the group rotation |
2184 | return new LSL_Rotation(q.X, q.Y, q.Z, q.W); | 2258 | |
2259 | return new LSL_Rotation(q); | ||
2185 | } | 2260 | } |
2186 | q = part.GetWorldRotation(); | 2261 | |
2187 | return new LSL_Rotation(q.X, q.Y, q.Z, q.W); | 2262 | return new LSL_Rotation(part.GetWorldRotation()); |
2188 | } | 2263 | } |
2189 | 2264 | ||
2190 | public LSL_Rotation llGetLocalRot() | 2265 | public LSL_Rotation llGetLocalRot() |
2191 | { | 2266 | { |
2192 | m_host.AddScriptLPS(1); | 2267 | m_host.AddScriptLPS(1); |
2193 | return new LSL_Rotation(m_host.RotationOffset.X, m_host.RotationOffset.Y, m_host.RotationOffset.Z, m_host.RotationOffset.W); | 2268 | |
2269 | return new LSL_Rotation(m_host.RotationOffset); | ||
2194 | } | 2270 | } |
2195 | 2271 | ||
2196 | public void llSetForce(LSL_Vector force, int local) | 2272 | public void llSetForce(LSL_Vector force, int local) |
@@ -2285,8 +2361,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
2285 | public LSL_Vector llGetTorque() | 2361 | public LSL_Vector llGetTorque() |
2286 | { | 2362 | { |
2287 | m_host.AddScriptLPS(1); | 2363 | m_host.AddScriptLPS(1); |
2288 | Vector3 torque = m_host.ParentGroup.GetTorque(); | 2364 | |
2289 | return new LSL_Vector(torque.X,torque.Y,torque.Z); | 2365 | return new LSL_Vector(m_host.ParentGroup.GetTorque()); |
2290 | } | 2366 | } |
2291 | 2367 | ||
2292 | public void llSetForceAndTorque(LSL_Vector force, LSL_Vector torque, int local) | 2368 | public void llSetForceAndTorque(LSL_Vector force, LSL_Vector torque, int local) |
@@ -2312,19 +2388,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
2312 | vel = m_host.Velocity; | 2388 | vel = m_host.Velocity; |
2313 | } | 2389 | } |
2314 | 2390 | ||
2315 | return new LSL_Vector(vel.X, vel.Y, vel.Z); | 2391 | return new LSL_Vector(vel); |
2316 | } | 2392 | } |
2317 | 2393 | ||
2318 | public LSL_Vector llGetAccel() | 2394 | public LSL_Vector llGetAccel() |
2319 | { | 2395 | { |
2320 | m_host.AddScriptLPS(1); | 2396 | m_host.AddScriptLPS(1); |
2321 | return new LSL_Vector(m_host.Acceleration.X, m_host.Acceleration.Y, m_host.Acceleration.Z); | 2397 | |
2398 | return new LSL_Vector(m_host.Acceleration); | ||
2322 | } | 2399 | } |
2323 | 2400 | ||
2324 | public LSL_Vector llGetOmega() | 2401 | public LSL_Vector llGetOmega() |
2325 | { | 2402 | { |
2326 | m_host.AddScriptLPS(1); | 2403 | m_host.AddScriptLPS(1); |
2327 | return new LSL_Vector(m_host.AngularVelocity.X, m_host.AngularVelocity.Y, m_host.AngularVelocity.Z); | 2404 | |
2405 | return new LSL_Vector(m_host.AngularVelocity); | ||
2328 | } | 2406 | } |
2329 | 2407 | ||
2330 | public LSL_Float llGetTimeOfDay() | 2408 | public LSL_Float llGetTimeOfDay() |
@@ -3101,13 +3179,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3101 | msg.ParentEstateID = 0; //ParentEstateID; | 3179 | msg.ParentEstateID = 0; //ParentEstateID; |
3102 | msg.Position = new Vector3(m_host.AbsolutePosition); | 3180 | msg.Position = new Vector3(m_host.AbsolutePosition); |
3103 | msg.RegionID = World.RegionInfo.RegionID.Guid;//RegionID.Guid; | 3181 | msg.RegionID = World.RegionInfo.RegionID.Guid;//RegionID.Guid; |
3182 | |||
3183 | Vector3 pos = m_host.AbsolutePosition; | ||
3104 | msg.binaryBucket | 3184 | msg.binaryBucket |
3105 | = Util.StringToBytes256( | 3185 | = Util.StringToBytes256( |
3106 | "{0}/{1}/{2}/{3}", | 3186 | "{0}/{1}/{2}/{3}", |
3107 | World.RegionInfo.RegionName, | 3187 | World.RegionInfo.RegionName, |
3108 | (int)Math.Floor(m_host.AbsolutePosition.X), | 3188 | (int)Math.Floor(pos.X), |
3109 | (int)Math.Floor(m_host.AbsolutePosition.Y), | 3189 | (int)Math.Floor(pos.Y), |
3110 | (int)Math.Floor(m_host.AbsolutePosition.Z)); | 3190 | (int)Math.Floor(pos.Z)); |
3111 | 3191 | ||
3112 | if (m_TransferModule != null) | 3192 | if (m_TransferModule != null) |
3113 | { | 3193 | { |
@@ -3691,47 +3771,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3691 | { | 3771 | { |
3692 | m_host.AddScriptLPS(1); | 3772 | m_host.AddScriptLPS(1); |
3693 | 3773 | ||
3694 | if (linknum < 0) | 3774 | ISceneEntity entity = GetLinkEntity(linknum); |
3695 | { | ||
3696 | if (linknum == ScriptBaseClass.LINK_THIS) | ||
3697 | return m_host.UUID.ToString(); | ||
3698 | else | ||
3699 | return ScriptBaseClass.NULL_KEY; | ||
3700 | } | ||
3701 | |||
3702 | int actualPrimCount = m_host.ParentGroup.PrimCount; | ||
3703 | List<UUID> sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars(); | ||
3704 | int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count; | ||
3705 | |||
3706 | // Special case for a single prim. In this case the linknum is zero. However, this will not match a single | ||
3707 | // prim that has any avatars sat upon it (in which case the root prim is link 1). | ||
3708 | if (linknum == 0) | ||
3709 | { | ||
3710 | if (actualPrimCount == 1 && sittingAvatarIds.Count == 0) | ||
3711 | return m_host.UUID.ToString(); | ||
3712 | 3775 | ||
3713 | return ScriptBaseClass.NULL_KEY; | 3776 | if (entity != null) |
3714 | } | 3777 | return entity.UUID.ToString(); |
3715 | // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but | ||
3716 | // here we must match 1 (ScriptBaseClass.LINK_ROOT). | ||
3717 | else if (linknum == 1 && actualPrimCount == 1) | ||
3718 | { | ||
3719 | if (sittingAvatarIds.Count > 0) | ||
3720 | return m_host.ParentGroup.RootPart.UUID.ToString(); | ||
3721 | else | ||
3722 | return ScriptBaseClass.NULL_KEY; | ||
3723 | } | ||
3724 | else if (linknum <= adjustedPrimCount) | ||
3725 | { | ||
3726 | if (linknum <= actualPrimCount) | ||
3727 | return m_host.ParentGroup.GetLinkNumPart(linknum).UUID.ToString(); | ||
3728 | else | ||
3729 | return sittingAvatarIds[linknum - actualPrimCount - 1].ToString(); | ||
3730 | } | ||
3731 | else | 3778 | else |
3732 | { | ||
3733 | return ScriptBaseClass.NULL_KEY; | 3779 | return ScriptBaseClass.NULL_KEY; |
3734 | } | ||
3735 | } | 3780 | } |
3736 | 3781 | ||
3737 | /// <summary> | 3782 | /// <summary> |
@@ -3777,55 +3822,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3777 | { | 3822 | { |
3778 | m_host.AddScriptLPS(1); | 3823 | m_host.AddScriptLPS(1); |
3779 | 3824 | ||
3780 | if (linknum < 0) | 3825 | ISceneEntity entity = GetLinkEntity(linknum); |
3781 | { | ||
3782 | if (linknum == ScriptBaseClass.LINK_THIS) | ||
3783 | return m_host.Name; | ||
3784 | else | ||
3785 | return ScriptBaseClass.NULL_KEY; | ||
3786 | } | ||
3787 | 3826 | ||
3788 | int actualPrimCount = m_host.ParentGroup.PrimCount; | 3827 | if (entity != null) |
3789 | List<UUID> sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars(); | 3828 | return entity.Name; |
3790 | int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count; | ||
3791 | |||
3792 | // Special case for a single prim. In this case the linknum is zero. However, this will not match a single | ||
3793 | // prim that has any avatars sat upon it (in which case the root prim is link 1). | ||
3794 | if (linknum == 0) | ||
3795 | { | ||
3796 | if (actualPrimCount == 1 && sittingAvatarIds.Count == 0) | ||
3797 | return m_host.Name; | ||
3798 | |||
3799 | return ScriptBaseClass.NULL_KEY; | ||
3800 | } | ||
3801 | // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but | ||
3802 | // here we must match 1 (ScriptBaseClass.LINK_ROOT). | ||
3803 | else if (linknum == 1 && actualPrimCount == 1) | ||
3804 | { | ||
3805 | if (sittingAvatarIds.Count > 0) | ||
3806 | return m_host.ParentGroup.RootPart.Name; | ||
3807 | else | ||
3808 | return ScriptBaseClass.NULL_KEY; | ||
3809 | } | ||
3810 | else if (linknum <= adjustedPrimCount) | ||
3811 | { | ||
3812 | if (linknum <= actualPrimCount) | ||
3813 | { | ||
3814 | return m_host.ParentGroup.GetLinkNumPart(linknum).Name; | ||
3815 | } | ||
3816 | else | ||
3817 | { | ||
3818 | ScenePresence sp = World.GetScenePresence(sittingAvatarIds[linknum - actualPrimCount - 1]); | ||
3819 | if (sp != null) | ||
3820 | return sp.Name; | ||
3821 | else | ||
3822 | return ScriptBaseClass.NULL_KEY; | ||
3823 | } | ||
3824 | } | ||
3825 | else | 3829 | else |
3826 | { | ||
3827 | return ScriptBaseClass.NULL_KEY; | 3830 | return ScriptBaseClass.NULL_KEY; |
3828 | } | ||
3829 | } | 3831 | } |
3830 | 3832 | ||
3831 | public LSL_Integer llGetInventoryNumber(int type) | 3833 | public LSL_Integer llGetInventoryNumber(int type) |
@@ -4170,13 +4172,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4170 | if (presence != null) | 4172 | if (presence != null) |
4171 | { | 4173 | { |
4172 | // agent must be over the owners land | 4174 | // agent must be over the owners land |
4173 | if (m_host.OwnerID == World.LandChannel.GetLandObject( | 4175 | if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) |
4174 | presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) | ||
4175 | { | 4176 | { |
4176 | World.TeleportClientHome(agentId, presence.ControllingClient); | 4177 | World.TeleportClientHome(agentId, presence.ControllingClient); |
4177 | } | 4178 | } |
4178 | } | 4179 | } |
4179 | } | 4180 | } |
4181 | |||
4180 | ScriptSleep(5000); | 4182 | ScriptSleep(5000); |
4181 | } | 4183 | } |
4182 | 4184 | ||
@@ -4197,8 +4199,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4197 | destination = World.RegionInfo.RegionName; | 4199 | destination = World.RegionInfo.RegionName; |
4198 | 4200 | ||
4199 | // agent must be over the owners land | 4201 | // agent must be over the owners land |
4200 | if (m_host.OwnerID == World.LandChannel.GetLandObject( | 4202 | if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) |
4201 | presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) | ||
4202 | { | 4203 | { |
4203 | DoLLTeleport(presence, destination, targetPos, targetLookAt); | 4204 | DoLLTeleport(presence, destination, targetPos, targetLookAt); |
4204 | } | 4205 | } |
@@ -4229,8 +4230,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4229 | if (presence.GodLevel >= 200) return; | 4230 | if (presence.GodLevel >= 200) return; |
4230 | 4231 | ||
4231 | // agent must be over the owners land | 4232 | // agent must be over the owners land |
4232 | if (m_host.OwnerID == World.LandChannel.GetLandObject( | 4233 | if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) |
4233 | presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) | ||
4234 | { | 4234 | { |
4235 | World.RequestTeleportLocation(presence.ControllingClient, regionHandle, targetPos, targetLookAt, (uint)TeleportFlags.ViaLocation); | 4235 | World.RequestTeleportLocation(presence.ControllingClient, regionHandle, targetPos, targetLookAt, (uint)TeleportFlags.ViaLocation); |
4236 | } | 4236 | } |
@@ -4434,7 +4434,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4434 | { | 4434 | { |
4435 | if (pushrestricted) | 4435 | if (pushrestricted) |
4436 | { | 4436 | { |
4437 | ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos.X, PusheePos.Y); | 4437 | ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos); |
4438 | 4438 | ||
4439 | // We didn't find the parcel but region is push restricted so assume it is NOT ok | 4439 | // We didn't find the parcel but region is push restricted so assume it is NOT ok |
4440 | if (targetlandObj == null) | 4440 | if (targetlandObj == null) |
@@ -4449,7 +4449,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4449 | } | 4449 | } |
4450 | else | 4450 | else |
4451 | { | 4451 | { |
4452 | ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos.X, PusheePos.Y); | 4452 | ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos); |
4453 | if (targetlandObj == null) | 4453 | if (targetlandObj == null) |
4454 | { | 4454 | { |
4455 | // We didn't find the parcel but region isn't push restricted so assume it's ok | 4455 | // We didn't find the parcel but region isn't push restricted so assume it's ok |
@@ -4871,8 +4871,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4871 | public LSL_Vector llGetCenterOfMass() | 4871 | public LSL_Vector llGetCenterOfMass() |
4872 | { | 4872 | { |
4873 | m_host.AddScriptLPS(1); | 4873 | m_host.AddScriptLPS(1); |
4874 | Vector3 center = m_host.GetCenterOfMass(); | 4874 | |
4875 | return new LSL_Vector(center.X,center.Y,center.Z); | 4875 | return new LSL_Vector(m_host.GetCenterOfMass()); |
4876 | } | 4876 | } |
4877 | 4877 | ||
4878 | public LSL_List llListSort(LSL_List src, int stride, int ascending) | 4878 | public LSL_List llListSort(LSL_List src, int stride, int ascending) |
@@ -5703,12 +5703,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
5703 | } | 5703 | } |
5704 | 5704 | ||
5705 | ILandObject land; | 5705 | ILandObject land; |
5706 | Vector3 pos; | ||
5707 | UUID id = UUID.Zero; | 5706 | UUID id = UUID.Zero; |
5707 | |||
5708 | if (parcel || parcelOwned) | 5708 | if (parcel || parcelOwned) |
5709 | { | 5709 | { |
5710 | pos = m_host.ParentGroup.RootPart.GetWorldPosition(); | 5710 | land = World.LandChannel.GetLandObject(m_host.ParentGroup.RootPart.GetWorldPosition()); |
5711 | land = World.LandChannel.GetLandObject(pos.X, pos.Y); | ||
5712 | if (land == null) | 5711 | if (land == null) |
5713 | { | 5712 | { |
5714 | id = UUID.Zero; | 5713 | id = UUID.Zero; |
@@ -5734,8 +5733,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
5734 | { | 5733 | { |
5735 | if (!regionWide) | 5734 | if (!regionWide) |
5736 | { | 5735 | { |
5737 | pos = ssp.AbsolutePosition; | 5736 | land = World.LandChannel.GetLandObject(ssp.AbsolutePosition); |
5738 | land = World.LandChannel.GetLandObject(pos.X, pos.Y); | ||
5739 | if (land != null) | 5737 | if (land != null) |
5740 | { | 5738 | { |
5741 | if (parcelOwned && land.LandData.OwnerID == id || | 5739 | if (parcelOwned && land.LandData.OwnerID == id || |
@@ -5860,7 +5858,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
5860 | if (presence != null) | 5858 | if (presence != null) |
5861 | { | 5859 | { |
5862 | // agent must be over the owners land | 5860 | // agent must be over the owners land |
5863 | ILandObject land = World.LandChannel.GetLandObject(presence.AbsolutePosition.X, presence.AbsolutePosition.Y); | 5861 | ILandObject land = World.LandChannel.GetLandObject(presence.AbsolutePosition); |
5864 | if (land == null) | 5862 | if (land == null) |
5865 | return; | 5863 | return; |
5866 | 5864 | ||
@@ -5882,19 +5880,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
5882 | ScenePresence presence = World.GetScenePresence(key); | 5880 | ScenePresence presence = World.GetScenePresence(key); |
5883 | if (presence != null) // object is an avatar | 5881 | if (presence != null) // object is an avatar |
5884 | { | 5882 | { |
5885 | if (m_host.OwnerID | 5883 | if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) |
5886 | == World.LandChannel.GetLandObject( | ||
5887 | presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) | ||
5888 | return 1; | 5884 | return 1; |
5889 | } | 5885 | } |
5890 | else // object is not an avatar | 5886 | else // object is not an avatar |
5891 | { | 5887 | { |
5892 | SceneObjectPart obj = World.GetSceneObjectPart(key); | 5888 | SceneObjectPart obj = World.GetSceneObjectPart(key); |
5889 | |||
5893 | if (obj != null) | 5890 | if (obj != null) |
5894 | if (m_host.OwnerID | 5891 | { |
5895 | == World.LandChannel.GetLandObject( | 5892 | if (m_host.OwnerID == World.LandChannel.GetLandObject(obj.AbsolutePosition).LandData.OwnerID) |
5896 | obj.AbsolutePosition.X, obj.AbsolutePosition.Y).LandData.OwnerID) | ||
5897 | return 1; | 5893 | return 1; |
5894 | } | ||
5898 | } | 5895 | } |
5899 | } | 5896 | } |
5900 | 5897 | ||
@@ -5972,8 +5969,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
5972 | // if the land is group owned and the object is group owned by the same group | 5969 | // if the land is group owned and the object is group owned by the same group |
5973 | // or | 5970 | // or |
5974 | // if the object is owned by a person with estate access. | 5971 | // if the object is owned by a person with estate access. |
5975 | 5972 | ILandObject parcel = World.LandChannel.GetLandObject(av.AbsolutePosition); | |
5976 | ILandObject parcel = World.LandChannel.GetLandObject(av.AbsolutePosition.X, av.AbsolutePosition.Y); | ||
5977 | if (parcel != null) | 5973 | if (parcel != null) |
5978 | { | 5974 | { |
5979 | if (m_host.OwnerID == parcel.LandData.OwnerID || | 5975 | if (m_host.OwnerID == parcel.LandData.OwnerID || |
@@ -5985,14 +5981,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
5985 | } | 5981 | } |
5986 | } | 5982 | } |
5987 | } | 5983 | } |
5988 | |||
5989 | } | 5984 | } |
5990 | |||
5991 | } | 5985 | } |
5992 | 5986 | ||
5993 | public LSL_Vector llGroundSlope(LSL_Vector offset) | 5987 | public LSL_Vector llGroundSlope(LSL_Vector offset) |
5994 | { | 5988 | { |
5995 | m_host.AddScriptLPS(1); | 5989 | m_host.AddScriptLPS(1); |
5990 | |||
5996 | //Get the slope normal. This gives us the equation of the plane tangent to the slope. | 5991 | //Get the slope normal. This gives us the equation of the plane tangent to the slope. |
5997 | LSL_Vector vsn = llGroundNormal(offset); | 5992 | LSL_Vector vsn = llGroundNormal(offset); |
5998 | 5993 | ||
@@ -6003,7 +5998,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
6003 | vsl.Normalize(); | 5998 | vsl.Normalize(); |
6004 | //Normalization might be overkill here | 5999 | //Normalization might be overkill here |
6005 | 6000 | ||
6006 | return new LSL_Vector(vsl.X, vsl.Y, vsl.Z); | 6001 | vsn.x = vsl.X; |
6002 | vsn.y = vsl.Y; | ||
6003 | vsn.z = vsl.Z; | ||
6004 | |||
6005 | return vsn; | ||
6007 | } | 6006 | } |
6008 | 6007 | ||
6009 | public LSL_Vector llGroundNormal(LSL_Vector offset) | 6008 | public LSL_Vector llGroundNormal(LSL_Vector offset) |
@@ -6053,7 +6052,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
6053 | //I believe the crossproduct of two normalized vectors is a normalized vector so | 6052 | //I believe the crossproduct of two normalized vectors is a normalized vector so |
6054 | //this normalization may be overkill | 6053 | //this normalization may be overkill |
6055 | 6054 | ||
6056 | return new LSL_Vector(vsn.X, vsn.Y, vsn.Z); | 6055 | return new LSL_Vector(vsn); |
6057 | } | 6056 | } |
6058 | 6057 | ||
6059 | public LSL_Vector llGroundContour(LSL_Vector offset) | 6058 | public LSL_Vector llGroundContour(LSL_Vector offset) |
@@ -6069,7 +6068,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
6069 | return m_host.ParentGroup.AttachmentPoint; | 6068 | return m_host.ParentGroup.AttachmentPoint; |
6070 | } | 6069 | } |
6071 | 6070 | ||
6072 | public LSL_Integer llGetFreeMemory() | 6071 | public virtual LSL_Integer llGetFreeMemory() |
6073 | { | 6072 | { |
6074 | m_host.AddScriptLPS(1); | 6073 | m_host.AddScriptLPS(1); |
6075 | // Make scripts designed for LSO happy | 6074 | // Make scripts designed for LSO happy |
@@ -6553,7 +6552,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
6553 | { | 6552 | { |
6554 | m_host.AddScriptLPS(1); | 6553 | m_host.AddScriptLPS(1); |
6555 | UUID key; | 6554 | UUID key; |
6556 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 6555 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
6556 | |||
6557 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) | 6557 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) |
6558 | { | 6558 | { |
6559 | int expires = 0; | 6559 | int expires = 0; |
@@ -7782,7 +7782,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
7782 | { | 7782 | { |
7783 | m_host.AddScriptLPS(1); | 7783 | m_host.AddScriptLPS(1); |
7784 | 7784 | ||
7785 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 7785 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
7786 | 7786 | ||
7787 | if (land.LandData.OwnerID != m_host.OwnerID) | 7787 | if (land.LandData.OwnerID != m_host.OwnerID) |
7788 | return; | 7788 | return; |
@@ -7796,7 +7796,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
7796 | { | 7796 | { |
7797 | m_host.AddScriptLPS(1); | 7797 | m_host.AddScriptLPS(1); |
7798 | 7798 | ||
7799 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 7799 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
7800 | 7800 | ||
7801 | if (land.LandData.OwnerID != m_host.OwnerID) | 7801 | if (land.LandData.OwnerID != m_host.OwnerID) |
7802 | return String.Empty; | 7802 | return String.Empty; |
@@ -7807,8 +7807,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
7807 | public LSL_Vector llGetRootPosition() | 7807 | public LSL_Vector llGetRootPosition() |
7808 | { | 7808 | { |
7809 | m_host.AddScriptLPS(1); | 7809 | m_host.AddScriptLPS(1); |
7810 | return new LSL_Vector(m_host.ParentGroup.AbsolutePosition.X, m_host.ParentGroup.AbsolutePosition.Y, | 7810 | |
7811 | m_host.ParentGroup.AbsolutePosition.Z); | 7811 | return new LSL_Vector(m_host.ParentGroup.AbsolutePosition); |
7812 | } | 7812 | } |
7813 | 7813 | ||
7814 | /// <summary> | 7814 | /// <summary> |
@@ -7831,13 +7831,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
7831 | if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0) | 7831 | if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0) |
7832 | q = avatar.CameraRotation; // Mouselook | 7832 | q = avatar.CameraRotation; // Mouselook |
7833 | else | 7833 | else |
7834 | q = avatar.Rotation; // Currently infrequently updated so may be inaccurate | 7834 | q = avatar.GetWorldRotation(); // Currently infrequently updated so may be inaccurate |
7835 | else | 7835 | else |
7836 | q = m_host.ParentGroup.GroupRotation; // Likely never get here but just in case | 7836 | q = m_host.ParentGroup.GroupRotation; // Likely never get here but just in case |
7837 | } | 7837 | } |
7838 | else | 7838 | else |
7839 | q = m_host.ParentGroup.GroupRotation; // just the group rotation | 7839 | q = m_host.ParentGroup.GroupRotation; // just the group rotation |
7840 | return new LSL_Rotation(q.X, q.Y, q.Z, q.W); | 7840 | |
7841 | return new LSL_Rotation(q); | ||
7841 | } | 7842 | } |
7842 | 7843 | ||
7843 | public LSL_String llGetObjectDesc() | 7844 | public LSL_String llGetObjectDesc() |
@@ -7944,7 +7945,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
7944 | 7945 | ||
7945 | public LSL_Vector llGetGeometricCenter() | 7946 | public LSL_Vector llGetGeometricCenter() |
7946 | { | 7947 | { |
7947 | return new LSL_Vector(m_host.GetGeometricCenter().X, m_host.GetGeometricCenter().Y, m_host.GetGeometricCenter().Z); | 7948 | return new LSL_Vector(m_host.GetGeometricCenter()); |
7948 | } | 7949 | } |
7949 | 7950 | ||
7950 | public LSL_List llGetPrimitiveParams(LSL_List rules) | 7951 | public LSL_List llGetPrimitiveParams(LSL_List rules) |
@@ -8031,23 +8032,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
8031 | break; | 8032 | break; |
8032 | 8033 | ||
8033 | case (int)ScriptBaseClass.PRIM_POSITION: | 8034 | case (int)ScriptBaseClass.PRIM_POSITION: |
8034 | LSL_Vector v = new LSL_Vector(part.AbsolutePosition.X, | 8035 | LSL_Vector v = new LSL_Vector(part.AbsolutePosition); |
8035 | part.AbsolutePosition.Y, | 8036 | |
8036 | part.AbsolutePosition.Z); | ||
8037 | // For some reason, the part.AbsolutePosition.* values do not change if the | 8037 | // For some reason, the part.AbsolutePosition.* values do not change if the |
8038 | // linkset is rotated; they always reflect the child prim's world position | 8038 | // linkset is rotated; they always reflect the child prim's world position |
8039 | // as though the linkset is unrotated. This is incompatible behavior with SL's | 8039 | // as though the linkset is unrotated. This is incompatible behavior with SL's |
8040 | // implementation, so will break scripts imported from there (not to mention it | 8040 | // implementation, so will break scripts imported from there (not to mention it |
8041 | // makes it more difficult to determine a child prim's actual inworld position). | 8041 | // makes it more difficult to determine a child prim's actual inworld position). |
8042 | if (part.ParentID != 0) | 8042 | if (!part.IsRoot) |
8043 | v = ((v - llGetRootPosition()) * llGetRootRotation()) + llGetRootPosition(); | 8043 | { |
8044 | LSL_Vector rootPos = new LSL_Vector(m_host.ParentGroup.AbsolutePosition); | ||
8045 | v = ((v - rootPos) * llGetRootRotation()) + rootPos; | ||
8046 | } | ||
8047 | |||
8044 | res.Add(v); | 8048 | res.Add(v); |
8045 | break; | 8049 | break; |
8046 | 8050 | ||
8047 | case (int)ScriptBaseClass.PRIM_SIZE: | 8051 | case (int)ScriptBaseClass.PRIM_SIZE: |
8048 | res.Add(new LSL_Vector(part.Scale.X, | 8052 | res.Add(new LSL_Vector(part.Scale)); |
8049 | part.Scale.Y, | ||
8050 | part.Scale.Z)); | ||
8051 | break; | 8053 | break; |
8052 | 8054 | ||
8053 | case (int)ScriptBaseClass.PRIM_ROTATION: | 8055 | case (int)ScriptBaseClass.PRIM_ROTATION: |
@@ -8361,8 +8363,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
8361 | case (int)ScriptBaseClass.PRIM_DESC: | 8363 | case (int)ScriptBaseClass.PRIM_DESC: |
8362 | res.Add(new LSL_String(part.Description)); | 8364 | res.Add(new LSL_String(part.Description)); |
8363 | break; | 8365 | break; |
8364 | case (int)ScriptBaseClass.PRIM_ROT_LOCAL: | 8366 | case (int)ScriptBaseClass.PRIM_ROT_LOCAL: |
8365 | res.Add(new LSL_Rotation(part.RotationOffset.X, part.RotationOffset.Y, part.RotationOffset.Z, part.RotationOffset.W)); | 8367 | res.Add(new LSL_Rotation(part.RotationOffset)); |
8366 | break; | 8368 | break; |
8367 | case (int)ScriptBaseClass.PRIM_POS_LOCAL: | 8369 | case (int)ScriptBaseClass.PRIM_POS_LOCAL: |
8368 | res.Add(new LSL_Vector(GetPartLocalPos(part))); | 8370 | res.Add(new LSL_Vector(GetPartLocalPos(part))); |
@@ -9571,7 +9573,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
9571 | 9573 | ||
9572 | // according to the docs, this command only works if script owner and land owner are the same | 9574 | // according to the docs, this command only works if script owner and land owner are the same |
9573 | // lets add estate owners and gods, too, and use the generic permission check. | 9575 | // lets add estate owners and gods, too, and use the generic permission check. |
9574 | ILandObject landObject = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 9576 | ILandObject landObject = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
9575 | if (!World.Permissions.CanEditParcelProperties(m_host.OwnerID, landObject, GroupPowers.ChangeMedia)) return; | 9577 | if (!World.Permissions.CanEditParcelProperties(m_host.OwnerID, landObject, GroupPowers.ChangeMedia)) return; |
9576 | 9578 | ||
9577 | bool update = false; // send a ParcelMediaUpdate (and possibly change the land's media URL)? | 9579 | bool update = false; // send a ParcelMediaUpdate (and possibly change the land's media URL)? |
@@ -9890,21 +9892,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
9890 | m_host.AddScriptLPS(1); | 9892 | m_host.AddScriptLPS(1); |
9891 | 9893 | ||
9892 | if (m_item.PermsGranter == UUID.Zero) | 9894 | if (m_item.PermsGranter == UUID.Zero) |
9893 | return new LSL_Vector(); | 9895 | return Vector3.Zero; |
9894 | 9896 | ||
9895 | if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0) | 9897 | if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0) |
9896 | { | 9898 | { |
9897 | ShoutError("No permissions to track the camera"); | 9899 | ShoutError("No permissions to track the camera"); |
9898 | return new LSL_Vector(); | 9900 | return Vector3.Zero; |
9899 | } | 9901 | } |
9900 | 9902 | ||
9901 | ScenePresence presence = World.GetScenePresence(m_host.OwnerID); | 9903 | ScenePresence presence = World.GetScenePresence(m_host.OwnerID); |
9902 | if (presence != null) | 9904 | if (presence != null) |
9903 | { | 9905 | { |
9904 | LSL_Vector pos = new LSL_Vector(presence.CameraPosition.X, presence.CameraPosition.Y, presence.CameraPosition.Z); | 9906 | LSL_Vector pos = new LSL_Vector(presence.CameraPosition); |
9905 | return pos; | 9907 | return pos; |
9906 | } | 9908 | } |
9907 | return new LSL_Vector(); | 9909 | |
9910 | return Vector3.Zero; | ||
9908 | } | 9911 | } |
9909 | 9912 | ||
9910 | public LSL_Rotation llGetCameraRot() | 9913 | public LSL_Rotation llGetCameraRot() |
@@ -9912,21 +9915,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
9912 | m_host.AddScriptLPS(1); | 9915 | m_host.AddScriptLPS(1); |
9913 | 9916 | ||
9914 | if (m_item.PermsGranter == UUID.Zero) | 9917 | if (m_item.PermsGranter == UUID.Zero) |
9915 | return new LSL_Rotation(); | 9918 | return Quaternion.Identity; |
9916 | 9919 | ||
9917 | if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0) | 9920 | if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0) |
9918 | { | 9921 | { |
9919 | ShoutError("No permissions to track the camera"); | 9922 | ShoutError("No permissions to track the camera"); |
9920 | return new LSL_Rotation(); | 9923 | return Quaternion.Identity; |
9921 | } | 9924 | } |
9922 | 9925 | ||
9923 | ScenePresence presence = World.GetScenePresence(m_host.OwnerID); | 9926 | ScenePresence presence = World.GetScenePresence(m_host.OwnerID); |
9924 | if (presence != null) | 9927 | if (presence != null) |
9925 | { | 9928 | { |
9926 | return new LSL_Rotation(presence.CameraRotation.X, presence.CameraRotation.Y, presence.CameraRotation.Z, presence.CameraRotation.W); | 9929 | return new LSL_Rotation(presence.CameraRotation); |
9927 | } | 9930 | } |
9928 | 9931 | ||
9929 | return new LSL_Rotation(); | 9932 | return Quaternion.Identity; |
9930 | } | 9933 | } |
9931 | 9934 | ||
9932 | /// <summary> | 9935 | /// <summary> |
@@ -9995,7 +9998,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
9995 | { | 9998 | { |
9996 | m_host.AddScriptLPS(1); | 9999 | m_host.AddScriptLPS(1); |
9997 | UUID key; | 10000 | UUID key; |
9998 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 10001 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
9999 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) | 10002 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) |
10000 | { | 10003 | { |
10001 | int expires = 0; | 10004 | int expires = 0; |
@@ -10036,7 +10039,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
10036 | { | 10039 | { |
10037 | m_host.AddScriptLPS(1); | 10040 | m_host.AddScriptLPS(1); |
10038 | UUID key; | 10041 | UUID key; |
10039 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 10042 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
10040 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageAllowed)) | 10043 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageAllowed)) |
10041 | { | 10044 | { |
10042 | if (UUID.TryParse(avatar, out key)) | 10045 | if (UUID.TryParse(avatar, out key)) |
@@ -10063,7 +10066,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
10063 | { | 10066 | { |
10064 | m_host.AddScriptLPS(1); | 10067 | m_host.AddScriptLPS(1); |
10065 | UUID key; | 10068 | UUID key; |
10066 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 10069 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
10067 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) | 10070 | if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) |
10068 | { | 10071 | { |
10069 | if (UUID.TryParse(avatar, out key)) | 10072 | if (UUID.TryParse(avatar, out key)) |
@@ -10325,7 +10328,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
10325 | public void llResetLandBanList() | 10328 | public void llResetLandBanList() |
10326 | { | 10329 | { |
10327 | m_host.AddScriptLPS(1); | 10330 | m_host.AddScriptLPS(1); |
10328 | LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).LandData; | 10331 | LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition).LandData; |
10329 | if (land.OwnerID == m_host.OwnerID) | 10332 | if (land.OwnerID == m_host.OwnerID) |
10330 | { | 10333 | { |
10331 | foreach (LandAccessEntry entry in land.ParcelAccessList) | 10334 | foreach (LandAccessEntry entry in land.ParcelAccessList) |
@@ -10342,7 +10345,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
10342 | public void llResetLandPassList() | 10345 | public void llResetLandPassList() |
10343 | { | 10346 | { |
10344 | m_host.AddScriptLPS(1); | 10347 | m_host.AddScriptLPS(1); |
10345 | LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).LandData; | 10348 | LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition).LandData; |
10346 | if (land.OwnerID == m_host.OwnerID) | 10349 | if (land.OwnerID == m_host.OwnerID) |
10347 | { | 10350 | { |
10348 | foreach (LandAccessEntry entry in land.ParcelAccessList) | 10351 | foreach (LandAccessEntry entry in land.ParcelAccessList) |
@@ -10518,7 +10521,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
10518 | ret.Add(new LSL_Vector((double)av.AbsolutePosition.X, (double)av.AbsolutePosition.Y, (double)av.AbsolutePosition.Z)); | 10521 | ret.Add(new LSL_Vector((double)av.AbsolutePosition.X, (double)av.AbsolutePosition.Y, (double)av.AbsolutePosition.Z)); |
10519 | break; | 10522 | break; |
10520 | case ScriptBaseClass.OBJECT_ROT: | 10523 | case ScriptBaseClass.OBJECT_ROT: |
10521 | ret.Add(new LSL_Rotation((double)av.Rotation.X, (double)av.Rotation.Y, (double)av.Rotation.Z, (double)av.Rotation.W)); | 10524 | ret.Add(new LSL_Rotation(av.GetWorldRotation())); |
10522 | break; | 10525 | break; |
10523 | case ScriptBaseClass.OBJECT_VELOCITY: | 10526 | case ScriptBaseClass.OBJECT_VELOCITY: |
10524 | ret.Add(new LSL_Vector(av.Velocity.X, av.Velocity.Y, av.Velocity.Z)); | 10527 | ret.Add(new LSL_Vector(av.Velocity.X, av.Velocity.Y, av.Velocity.Z)); |
@@ -10920,7 +10923,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
10920 | 10923 | ||
10921 | LSL_List result = new LSL_List(); | 10924 | LSL_List result = new LSL_List(); |
10922 | 10925 | ||
10923 | if (obj != null && obj.OwnerID != m_host.OwnerID) | 10926 | if (obj != null && obj.OwnerID == m_host.OwnerID) |
10924 | { | 10927 | { |
10925 | LSL_List remaining = GetPrimParams(obj, rules, ref result); | 10928 | LSL_List remaining = GetPrimParams(obj, rules, ref result); |
10926 | 10929 | ||
@@ -11021,7 +11024,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
11021 | World.ForEachScenePresence(delegate(ScenePresence sp) | 11024 | World.ForEachScenePresence(delegate(ScenePresence sp) |
11022 | { | 11025 | { |
11023 | Vector3 ac = sp.AbsolutePosition - rayStart; | 11026 | Vector3 ac = sp.AbsolutePosition - rayStart; |
11024 | Vector3 bc = sp.AbsolutePosition - rayEnd; | 11027 | // Vector3 bc = sp.AbsolutePosition - rayEnd; |
11025 | 11028 | ||
11026 | double d = Math.Abs(Vector3.Mag(Vector3.Cross(ab, ac)) / Vector3.Distance(rayStart, rayEnd)); | 11029 | double d = Math.Abs(Vector3.Mag(Vector3.Cross(ab, ac)) / Vector3.Distance(rayStart, rayEnd)); |
11027 | 11030 | ||
@@ -11111,7 +11114,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
11111 | radius = Math.Abs(maxZ); | 11114 | radius = Math.Abs(maxZ); |
11112 | radius = radius*1.413f; | 11115 | radius = radius*1.413f; |
11113 | Vector3 ac = group.AbsolutePosition - rayStart; | 11116 | Vector3 ac = group.AbsolutePosition - rayStart; |
11114 | Vector3 bc = group.AbsolutePosition - rayEnd; | 11117 | // Vector3 bc = group.AbsolutePosition - rayEnd; |
11115 | 11118 | ||
11116 | double d = Math.Abs(Vector3.Mag(Vector3.Cross(ab, ac)) / Vector3.Distance(rayStart, rayEnd)); | 11119 | double d = Math.Abs(Vector3.Mag(Vector3.Cross(ab, ac)) / Vector3.Distance(rayStart, rayEnd)); |
11117 | 11120 | ||
@@ -11455,7 +11458,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
11455 | list.Add(new LSL_Integer(linkNum)); | 11458 | list.Add(new LSL_Integer(linkNum)); |
11456 | 11459 | ||
11457 | if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL) | 11460 | if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL) |
11458 | list.Add(new LSL_Vector(result.Normal.X, result.Normal.Y, result.Normal.Z)); | 11461 | list.Add(new LSL_Vector(result.Normal)); |
11459 | 11462 | ||
11460 | values++; | 11463 | values++; |
11461 | if (values >= count) | 11464 | if (values >= count) |
@@ -11557,7 +11560,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
11557 | return 16384; | 11560 | return 16384; |
11558 | } | 11561 | } |
11559 | 11562 | ||
11560 | public LSL_Integer llGetUsedMemory() | 11563 | public virtual LSL_Integer llGetUsedMemory() |
11561 | { | 11564 | { |
11562 | m_host.AddScriptLPS(1); | 11565 | m_host.AddScriptLPS(1); |
11563 | // The value returned for LSO scripts in SL | 11566 | // The value returned for LSO scripts in SL |
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs index d0922aa..21bae27 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs | |||
@@ -266,6 +266,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
266 | { | 266 | { |
267 | llist[i] = new LSL_Float((float)result[i]); | 267 | llist[i] = new LSL_Float((float)result[i]); |
268 | } | 268 | } |
269 | else if (result[i] is double) | ||
270 | { | ||
271 | llist[i] = new LSL_Float((double)result[i]); | ||
272 | } | ||
269 | else if (result[i] is UUID) | 273 | else if (result[i] is UUID) |
270 | { | 274 | { |
271 | llist[i] = new LSL_Key(result[i].ToString()); | 275 | llist[i] = new LSL_Key(result[i].ToString()); |
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 48c6b50..bf1b45b 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs | |||
@@ -363,7 +363,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
363 | //OSSL only may be used if object is in the same group as the parcel | 363 | //OSSL only may be used if object is in the same group as the parcel |
364 | if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("PARCEL_GROUP_MEMBER")) | 364 | if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("PARCEL_GROUP_MEMBER")) |
365 | { | 365 | { |
366 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 366 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
367 | 367 | ||
368 | if (land.LandData.GroupID == m_item.GroupID && land.LandData.GroupID != UUID.Zero) | 368 | if (land.LandData.GroupID == m_item.GroupID && land.LandData.GroupID != UUID.Zero) |
369 | { | 369 | { |
@@ -374,7 +374,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
374 | //Only Parcelowners may use the function | 374 | //Only Parcelowners may use the function |
375 | if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("PARCEL_OWNER")) | 375 | if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("PARCEL_OWNER")) |
376 | { | 376 | { |
377 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | 377 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
378 | 378 | ||
379 | if (land.LandData.OwnerID == ownerID) | 379 | if (land.LandData.OwnerID == ownerID) |
380 | { | 380 | { |
@@ -1502,8 +1502,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
1502 | 1502 | ||
1503 | m_host.AddScriptLPS(1); | 1503 | m_host.AddScriptLPS(1); |
1504 | 1504 | ||
1505 | ILandObject land | 1505 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
1506 | = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | ||
1507 | 1506 | ||
1508 | if (land.LandData.OwnerID != m_host.OwnerID) | 1507 | if (land.LandData.OwnerID != m_host.OwnerID) |
1509 | return; | 1508 | return; |
@@ -1519,8 +1518,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
1519 | 1518 | ||
1520 | m_host.AddScriptLPS(1); | 1519 | m_host.AddScriptLPS(1); |
1521 | 1520 | ||
1522 | ILandObject land | 1521 | ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); |
1523 | = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); | ||
1524 | 1522 | ||
1525 | if (land.LandData.OwnerID != m_host.OwnerID) | 1523 | if (land.LandData.OwnerID != m_host.OwnerID) |
1526 | { | 1524 | { |
@@ -2515,13 +2513,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
2515 | ScenePresence sp = World.GetScenePresence(npcId); | 2513 | ScenePresence sp = World.GetScenePresence(npcId); |
2516 | 2514 | ||
2517 | if (sp != null) | 2515 | if (sp != null) |
2518 | { | 2516 | return new LSL_Vector(sp.AbsolutePosition); |
2519 | Vector3 pos = sp.AbsolutePosition; | ||
2520 | return new LSL_Vector(pos.X, pos.Y, pos.Z); | ||
2521 | } | ||
2522 | } | 2517 | } |
2523 | 2518 | ||
2524 | return new LSL_Vector(0, 0, 0); | 2519 | return Vector3.Zero; |
2525 | } | 2520 | } |
2526 | 2521 | ||
2527 | public void osNpcMoveTo(LSL_Key npc, LSL_Vector pos) | 2522 | public void osNpcMoveTo(LSL_Key npc, LSL_Vector pos) |
@@ -2578,21 +2573,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
2578 | { | 2573 | { |
2579 | UUID npcId; | 2574 | UUID npcId; |
2580 | if (!UUID.TryParse(npc.m_string, out npcId)) | 2575 | if (!UUID.TryParse(npc.m_string, out npcId)) |
2581 | return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W); | 2576 | return new LSL_Rotation(Quaternion.Identity); |
2582 | 2577 | ||
2583 | if (!npcModule.CheckPermissions(npcId, m_host.OwnerID)) | 2578 | if (!npcModule.CheckPermissions(npcId, m_host.OwnerID)) |
2584 | return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W); | 2579 | return new LSL_Rotation(Quaternion.Identity); |
2585 | 2580 | ||
2586 | ScenePresence sp = World.GetScenePresence(npcId); | 2581 | ScenePresence sp = World.GetScenePresence(npcId); |
2587 | 2582 | ||
2588 | if (sp != null) | 2583 | if (sp != null) |
2589 | { | 2584 | return new LSL_Rotation(sp.GetWorldRotation()); |
2590 | Quaternion rot = sp.Rotation; | ||
2591 | return new LSL_Rotation(rot.X, rot.Y, rot.Z, rot.W); | ||
2592 | } | ||
2593 | } | 2585 | } |
2594 | 2586 | ||
2595 | return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W); | 2587 | return Quaternion.Identity; |
2596 | } | 2588 | } |
2597 | 2589 | ||
2598 | public void osNpcSetRot(LSL_Key npc, LSL_Rotation rotation) | 2590 | public void osNpcSetRot(LSL_Key npc, LSL_Rotation rotation) |
@@ -3022,20 +3014,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3022 | 3014 | ||
3023 | UUID avatarId = new UUID(avatar); | 3015 | UUID avatarId = new UUID(avatar); |
3024 | ScenePresence presence = World.GetScenePresence(avatarId); | 3016 | ScenePresence presence = World.GetScenePresence(avatarId); |
3025 | Vector3 pos = m_host.GetWorldPosition(); | 3017 | |
3026 | bool result = World.ScriptDanger(m_host.LocalId, new Vector3((float)pos.X, (float)pos.Y, (float)pos.Z)); | 3018 | if (presence != null && World.ScriptDanger(m_host.LocalId, m_host.GetWorldPosition())) |
3027 | if (result) | ||
3028 | { | 3019 | { |
3029 | if (presence != null) | 3020 | float health = presence.Health; |
3030 | { | 3021 | health += (float)healing; |
3031 | float health = presence.Health; | 3022 | |
3032 | health += (float)healing; | 3023 | if (health >= 100) |
3033 | if (health >= 100) | 3024 | health = 100; |
3034 | { | 3025 | |
3035 | health = 100; | 3026 | presence.setHealthWithUpdate(health); |
3036 | } | ||
3037 | presence.setHealthWithUpdate(health); | ||
3038 | } | ||
3039 | } | 3027 | } |
3040 | } | 3028 | } |
3041 | 3029 | ||
@@ -3112,8 +3100,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3112 | if (avatar != null && avatar.UUID != m_host.OwnerID) | 3100 | if (avatar != null && avatar.UUID != m_host.OwnerID) |
3113 | { | 3101 | { |
3114 | result.Add(new LSL_String(avatar.UUID.ToString())); | 3102 | result.Add(new LSL_String(avatar.UUID.ToString())); |
3115 | OpenMetaverse.Vector3 ap = avatar.AbsolutePosition; | 3103 | result.Add(new LSL_Vector(avatar.AbsolutePosition)); |
3116 | result.Add(new LSL_Vector(ap.X, ap.Y, ap.Z)); | ||
3117 | result.Add(new LSL_String(avatar.Name)); | 3104 | result.Add(new LSL_String(avatar.Name)); |
3118 | } | 3105 | } |
3119 | }); | 3106 | }); |
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index dd45406..88ab515 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs | |||
@@ -353,7 +353,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins | |||
353 | // Position of a sensor in a child prim attached to an avatar | 353 | // Position of a sensor in a child prim attached to an avatar |
354 | // will be still wrong. | 354 | // will be still wrong. |
355 | ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar); | 355 | ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar); |
356 | q = avatar.Rotation * q; | 356 | q = avatar.GetWorldRotation() * q; |
357 | } | 357 | } |
358 | 358 | ||
359 | LSL_Types.Quaternion r = new LSL_Types.Quaternion(q); | 359 | LSL_Types.Quaternion r = new LSL_Types.Quaternion(q); |
@@ -480,7 +480,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins | |||
480 | // Position of a sensor in a child prim attached to an avatar | 480 | // Position of a sensor in a child prim attached to an avatar |
481 | // will be still wrong. | 481 | // will be still wrong. |
482 | ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar); | 482 | ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar); |
483 | q = avatar.Rotation * q; | 483 | q = avatar.GetWorldRotation() * q; |
484 | } | 484 | } |
485 | 485 | ||
486 | LSL_Types.Quaternion r = new LSL_Types.Quaternion(q); | 486 | LSL_Types.Quaternion r = new LSL_Types.Quaternion(q); |
diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs index 9d20c9e..b71afe3 100644 --- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs +++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs | |||
@@ -662,13 +662,18 @@ namespace SecondLife | |||
662 | { | 662 | { |
663 | string severity = CompErr.IsWarning ? "Warning" : "Error"; | 663 | string severity = CompErr.IsWarning ? "Warning" : "Error"; |
664 | 664 | ||
665 | KeyValuePair<int, int> lslPos; | 665 | KeyValuePair<int, int> errorPos; |
666 | 666 | ||
667 | // Show 5 errors max, but check entire list for errors | 667 | // Show 5 errors max, but check entire list for errors |
668 | 668 | ||
669 | if (severity == "Error") | 669 | if (severity == "Error") |
670 | { | 670 | { |
671 | lslPos = FindErrorPosition(CompErr.Line, CompErr.Column, m_lineMaps[assembly]); | 671 | // C# scripts will not have a linemap since theres no line translation involved. |
672 | if (!m_lineMaps.ContainsKey(assembly)) | ||
673 | errorPos = new KeyValuePair<int, int>(CompErr.Line, CompErr.Column); | ||
674 | else | ||
675 | errorPos = FindErrorPosition(CompErr.Line, CompErr.Column, m_lineMaps[assembly]); | ||
676 | |||
672 | string text = CompErr.ErrorText; | 677 | string text = CompErr.ErrorText; |
673 | 678 | ||
674 | // Use LSL type names | 679 | // Use LSL type names |
@@ -678,7 +683,7 @@ namespace SecondLife | |||
678 | // The Second Life viewer's script editor begins | 683 | // The Second Life viewer's script editor begins |
679 | // countingn lines and columns at 0, so we subtract 1. | 684 | // countingn lines and columns at 0, so we subtract 1. |
680 | errtext += String.Format("({0},{1}): {4} {2}: {3}\n", | 685 | errtext += String.Format("({0},{1}): {4} {2}: {3}\n", |
681 | lslPos.Key - 1, lslPos.Value - 1, | 686 | errorPos.Key - 1, errorPos.Value - 1, |
682 | CompErr.ErrorNumber, text, severity); | 687 | CompErr.ErrorNumber, text, severity); |
683 | hadErrors = true; | 688 | hadErrors = true; |
684 | } | 689 | } |
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiHttpTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiHttpTests.cs index b0baa1c..ab44e38 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiHttpTests.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiHttpTests.cs | |||
@@ -209,7 +209,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests | |||
209 | += (itemId, evp) => m_lslApi.llHTTPResponse(evp.Params[0].ToString(), 200, testResponse); | 209 | += (itemId, evp) => m_lslApi.llHTTPResponse(evp.Params[0].ToString(), 200, testResponse); |
210 | 210 | ||
211 | // Console.WriteLine("Trying {0}", returnedUri); | 211 | // Console.WriteLine("Trying {0}", returnedUri); |
212 | HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(returnedUri); | ||
213 | 212 | ||
214 | AssertHttpResponse(returnedUri, testResponse); | 213 | AssertHttpResponse(returnedUri, testResponse); |
215 | 214 | ||
diff --git a/OpenSim/Server/Base/ServerUtils.cs b/OpenSim/Server/Base/ServerUtils.cs index 2e6d279..210a314 100644 --- a/OpenSim/Server/Base/ServerUtils.cs +++ b/OpenSim/Server/Base/ServerUtils.cs | |||
@@ -138,17 +138,17 @@ namespace OpenSim.Server.Base | |||
138 | case ExtensionChange.Add: | 138 | case ExtensionChange.Add: |
139 | if (a.AddinFile.Contains(Registry.DefaultAddinsFolder)) | 139 | if (a.AddinFile.Contains(Registry.DefaultAddinsFolder)) |
140 | { | 140 | { |
141 | m_log.InfoFormat("[SERVER]: Adding {0} from registry", a.Name); | 141 | m_log.InfoFormat("[SERVER UTILS]: Adding {0} from registry", a.Name); |
142 | connector.PluginPath = System.IO.Path.Combine(Registry.DefaultAddinsFolder,a.Name.Replace(',', '.')); } | 142 | connector.PluginPath = System.IO.Path.Combine(Registry.DefaultAddinsFolder,a.Name.Replace(',', '.')); } |
143 | else | 143 | else |
144 | { | 144 | { |
145 | m_log.InfoFormat("[SERVER]: Adding {0} from ./bin", a.Name); | 145 | m_log.InfoFormat("[SERVER UTILS]: Adding {0} from ./bin", a.Name); |
146 | connector.PluginPath = a.AddinFile; | 146 | connector.PluginPath = a.AddinFile; |
147 | } | 147 | } |
148 | LoadPlugin(connector); | 148 | LoadPlugin(connector); |
149 | break; | 149 | break; |
150 | case ExtensionChange.Remove: | 150 | case ExtensionChange.Remove: |
151 | m_log.InfoFormat("[SERVER]: Removing {0}", a.Name); | 151 | m_log.InfoFormat("[SERVER UTILS]: Removing {0}", a.Name); |
152 | UnloadPlugin(connector); | 152 | UnloadPlugin(connector); |
153 | break; | 153 | break; |
154 | } | 154 | } |
@@ -166,13 +166,13 @@ namespace OpenSim.Server.Base | |||
166 | } | 166 | } |
167 | else | 167 | else |
168 | { | 168 | { |
169 | m_log.InfoFormat("[SERVER]: {0} Disabled.", connector.ConfigName); | 169 | m_log.InfoFormat("[SERVER UTILS]: {0} Disabled.", connector.ConfigName); |
170 | } | 170 | } |
171 | } | 171 | } |
172 | 172 | ||
173 | private void UnloadPlugin(IRobustConnector connector) | 173 | private void UnloadPlugin(IRobustConnector connector) |
174 | { | 174 | { |
175 | m_log.InfoFormat("[Server]: Unloading {0}", connector.ConfigName); | 175 | m_log.InfoFormat("[SERVER UTILS]: Unloading {0}", connector.ConfigName); |
176 | 176 | ||
177 | connector.Unload(); | 177 | connector.Unload(); |
178 | } | 178 | } |
@@ -280,7 +280,7 @@ namespace OpenSim.Server.Base | |||
280 | { | 280 | { |
281 | if (!(e is System.MissingMethodException)) | 281 | if (!(e is System.MissingMethodException)) |
282 | { | 282 | { |
283 | m_log.ErrorFormat("Error loading plugin {0} from {1}. Exception: {2}, {3}", | 283 | m_log.ErrorFormat("[SERVER UTILS]: Error loading plugin {0} from {1}. Exception: {2}, {3}", |
284 | interfaceName, | 284 | interfaceName, |
285 | dllName, | 285 | dllName, |
286 | e.InnerException == null ? e.Message : e.InnerException.Message, | 286 | e.InnerException == null ? e.Message : e.InnerException.Message, |
@@ -298,14 +298,14 @@ namespace OpenSim.Server.Base | |||
298 | } | 298 | } |
299 | catch (ReflectionTypeLoadException rtle) | 299 | catch (ReflectionTypeLoadException rtle) |
300 | { | 300 | { |
301 | m_log.Error(string.Format("Error loading plugin from {0}:\n{1}", dllName, | 301 | m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}:\n{1}", dllName, |
302 | String.Join("\n", Array.ConvertAll(rtle.LoaderExceptions, e => e.ToString()))), | 302 | String.Join("\n", Array.ConvertAll(rtle.LoaderExceptions, e => e.ToString()))), |
303 | rtle); | 303 | rtle); |
304 | return null; | 304 | return null; |
305 | } | 305 | } |
306 | catch (Exception e) | 306 | catch (Exception e) |
307 | { | 307 | { |
308 | m_log.Error(string.Format("Error loading plugin from {0}", dllName), e); | 308 | m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}", dllName), e); |
309 | return null; | 309 | return null; |
310 | } | 310 | } |
311 | } | 311 | } |
@@ -517,7 +517,7 @@ namespace OpenSim.Server.Base | |||
517 | public static IConfigSource LoadInitialConfig(string url) | 517 | public static IConfigSource LoadInitialConfig(string url) |
518 | { | 518 | { |
519 | IConfigSource source = new XmlConfigSource(); | 519 | IConfigSource source = new XmlConfigSource(); |
520 | m_log.InfoFormat("[CONFIG]: {0} is a http:// URI, fetching ...", url); | 520 | m_log.InfoFormat("[SERVER UTILS]: {0} is a http:// URI, fetching ...", url); |
521 | 521 | ||
522 | // The ini file path is a http URI | 522 | // The ini file path is a http URI |
523 | // Try to read it | 523 | // Try to read it |
@@ -529,7 +529,7 @@ namespace OpenSim.Server.Base | |||
529 | } | 529 | } |
530 | catch (Exception e) | 530 | catch (Exception e) |
531 | { | 531 | { |
532 | m_log.FatalFormat("[CONFIG]: Exception reading config from URI {0}\n" + e.ToString(), url); | 532 | m_log.FatalFormat("[SERVER UTILS]: Exception reading config from URI {0}\n" + e.ToString(), url); |
533 | Environment.Exit(1); | 533 | Environment.Exit(1); |
534 | } | 534 | } |
535 | 535 | ||
diff --git a/OpenSim/Server/Base/ServicesServerBase.cs b/OpenSim/Server/Base/ServicesServerBase.cs index 5aff72a..7c8e6b7 100644 --- a/OpenSim/Server/Base/ServicesServerBase.cs +++ b/OpenSim/Server/Base/ServicesServerBase.cs | |||
@@ -186,10 +186,7 @@ namespace OpenSim.Server.Base | |||
186 | XmlConfigurator.Configure(); | 186 | XmlConfigurator.Configure(); |
187 | } | 187 | } |
188 | 188 | ||
189 | // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net | 189 | LogEnvironmentInformation(); |
190 | // XmlConfigurator calls first accross servers. | ||
191 | m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory); | ||
192 | |||
193 | RegisterCommonAppenders(startupConfig); | 190 | RegisterCommonAppenders(startupConfig); |
194 | 191 | ||
195 | if (startupConfig.GetString("PIDFile", String.Empty) != String.Empty) | 192 | if (startupConfig.GetString("PIDFile", String.Empty) != String.Empty) |
diff --git a/OpenSim/Server/ServerMain.cs b/OpenSim/Server/ServerMain.cs index 8be69a9..65e9287 100644 --- a/OpenSim/Server/ServerMain.cs +++ b/OpenSim/Server/ServerMain.cs | |||
@@ -145,7 +145,7 @@ namespace OpenSim.Server | |||
145 | } | 145 | } |
146 | else | 146 | else |
147 | { | 147 | { |
148 | m_log.InfoFormat("[SERVER]: Failed to load {0}", conn); | 148 | m_log.ErrorFormat("[SERVER]: Failed to load {0}", conn); |
149 | } | 149 | } |
150 | } | 150 | } |
151 | 151 | ||
diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index e7eb6fe..08fd3f8 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs | |||
@@ -123,46 +123,32 @@ namespace OpenSim.Services.AssetService | |||
123 | public virtual AssetMetadata GetMetadata(string id) | 123 | public virtual AssetMetadata GetMetadata(string id) |
124 | { | 124 | { |
125 | // m_log.DebugFormat("[ASSET SERVICE]: Get asset metadata for {0}", id); | 125 | // m_log.DebugFormat("[ASSET SERVICE]: Get asset metadata for {0}", id); |
126 | |||
127 | UUID assetID; | ||
128 | 126 | ||
129 | if (!UUID.TryParse(id, out assetID)) | 127 | AssetBase asset = Get(id); |
130 | return null; | ||
131 | 128 | ||
132 | AssetBase asset = m_Database.GetAsset(assetID); | ||
133 | if (asset != null) | 129 | if (asset != null) |
134 | return asset.Metadata; | 130 | return asset.Metadata; |
135 | 131 | else | |
136 | return null; | 132 | return null; |
137 | } | 133 | } |
138 | 134 | ||
139 | public virtual byte[] GetData(string id) | 135 | public virtual byte[] GetData(string id) |
140 | { | 136 | { |
141 | // m_log.DebugFormat("[ASSET SERVICE]: Get asset data for {0}", id); | 137 | // m_log.DebugFormat("[ASSET SERVICE]: Get asset data for {0}", id); |
142 | |||
143 | UUID assetID; | ||
144 | 138 | ||
145 | if (!UUID.TryParse(id, out assetID)) | 139 | AssetBase asset = Get(id); |
146 | return null; | ||
147 | 140 | ||
148 | AssetBase asset = m_Database.GetAsset(assetID); | 141 | if (asset != null) |
149 | return asset.Data; | 142 | return asset.Data; |
143 | else | ||
144 | return null; | ||
150 | } | 145 | } |
151 | 146 | ||
152 | public virtual bool Get(string id, Object sender, AssetRetrieved handler) | 147 | public virtual bool Get(string id, Object sender, AssetRetrieved handler) |
153 | { | 148 | { |
154 | //m_log.DebugFormat("[AssetService]: Get asset async {0}", id); | 149 | //m_log.DebugFormat("[AssetService]: Get asset async {0}", id); |
155 | |||
156 | UUID assetID; | ||
157 | 150 | ||
158 | if (!UUID.TryParse(id, out assetID)) | 151 | handler(id, sender, Get(id)); |
159 | return false; | ||
160 | |||
161 | AssetBase asset = m_Database.GetAsset(assetID); | ||
162 | |||
163 | //m_log.DebugFormat("[AssetService]: Got asset {0}", asset); | ||
164 | |||
165 | handler(id, sender, asset); | ||
166 | 152 | ||
167 | return true; | 153 | return true; |
168 | } | 154 | } |
diff --git a/OpenSim/Services/AssetService/XAssetService.cs b/OpenSim/Services/AssetService/XAssetService.cs index a1d10ed..8a2ca7c 100644 --- a/OpenSim/Services/AssetService/XAssetService.cs +++ b/OpenSim/Services/AssetService/XAssetService.cs | |||
@@ -39,8 +39,7 @@ using OpenMetaverse; | |||
39 | namespace OpenSim.Services.AssetService | 39 | namespace OpenSim.Services.AssetService |
40 | { | 40 | { |
41 | /// <summary> | 41 | /// <summary> |
42 | /// This will be developed into a de-duplicating asset service. | 42 | /// A de-duplicating asset service. |
43 | /// XXX: Currently it's a just a copy of the existing AssetService. so please don't attempt to use it. | ||
44 | /// </summary> | 43 | /// </summary> |
45 | public class XAssetService : XAssetServiceBase, IAssetService | 44 | public class XAssetService : XAssetServiceBase, IAssetService |
46 | { | 45 | { |
@@ -48,7 +47,9 @@ namespace OpenSim.Services.AssetService | |||
48 | 47 | ||
49 | protected static XAssetService m_RootInstance; | 48 | protected static XAssetService m_RootInstance; |
50 | 49 | ||
51 | public XAssetService(IConfigSource config) : base(config) | 50 | public XAssetService(IConfigSource config) : this(config, "AssetService") {} |
51 | |||
52 | public XAssetService(IConfigSource config, string configName) : base(config, configName) | ||
52 | { | 53 | { |
53 | if (m_RootInstance == null) | 54 | if (m_RootInstance == null) |
54 | { | 55 | { |
@@ -56,22 +57,21 @@ namespace OpenSim.Services.AssetService | |||
56 | 57 | ||
57 | if (m_AssetLoader != null) | 58 | if (m_AssetLoader != null) |
58 | { | 59 | { |
59 | IConfig assetConfig = config.Configs["AssetService"]; | 60 | IConfig assetConfig = config.Configs[configName]; |
60 | if (assetConfig == null) | 61 | if (assetConfig == null) |
61 | throw new Exception("No AssetService configuration"); | 62 | throw new Exception("No AssetService configuration"); |
62 | 63 | ||
63 | string loaderArgs = assetConfig.GetString("AssetLoaderArgs", | 64 | string loaderArgs = assetConfig.GetString("AssetLoaderArgs", String.Empty); |
64 | String.Empty); | ||
65 | 65 | ||
66 | bool assetLoaderEnabled = assetConfig.GetBoolean("AssetLoaderEnabled", true); | 66 | bool assetLoaderEnabled = assetConfig.GetBoolean("AssetLoaderEnabled", true); |
67 | 67 | ||
68 | if (assetLoaderEnabled) | 68 | if (assetLoaderEnabled && !HasChainedAssetService) |
69 | { | 69 | { |
70 | m_log.DebugFormat("[XASSET SERVICE]: Loading default asset set from {0}", loaderArgs); | 70 | m_log.DebugFormat("[XASSET SERVICE]: Loading default asset set from {0}", loaderArgs); |
71 | 71 | ||
72 | m_AssetLoader.ForEachDefaultXmlAsset( | 72 | m_AssetLoader.ForEachDefaultXmlAsset( |
73 | loaderArgs, | 73 | loaderArgs, |
74 | delegate(AssetBase a) | 74 | a => |
75 | { | 75 | { |
76 | AssetBase existingAsset = Get(a.ID); | 76 | AssetBase existingAsset = Get(a.ID); |
77 | // AssetMetadata existingMetadata = GetMetadata(a.ID); | 77 | // AssetMetadata existingMetadata = GetMetadata(a.ID); |
@@ -103,7 +103,23 @@ namespace OpenSim.Services.AssetService | |||
103 | 103 | ||
104 | try | 104 | try |
105 | { | 105 | { |
106 | return m_Database.GetAsset(assetID); | 106 | AssetBase asset = m_Database.GetAsset(assetID); |
107 | |||
108 | if (asset != null) | ||
109 | { | ||
110 | return asset; | ||
111 | } | ||
112 | else if (HasChainedAssetService) | ||
113 | { | ||
114 | asset = m_ChainedAssetService.Get(id); | ||
115 | |||
116 | if (asset != null) | ||
117 | MigrateFromChainedService(asset); | ||
118 | |||
119 | return asset; | ||
120 | } | ||
121 | |||
122 | return null; | ||
107 | } | 123 | } |
108 | catch (Exception e) | 124 | catch (Exception e) |
109 | { | 125 | { |
@@ -120,30 +136,25 @@ namespace OpenSim.Services.AssetService | |||
120 | public virtual AssetMetadata GetMetadata(string id) | 136 | public virtual AssetMetadata GetMetadata(string id) |
121 | { | 137 | { |
122 | // m_log.DebugFormat("[XASSET SERVICE]: Get asset metadata for {0}", id); | 138 | // m_log.DebugFormat("[XASSET SERVICE]: Get asset metadata for {0}", id); |
123 | |||
124 | UUID assetID; | ||
125 | 139 | ||
126 | if (!UUID.TryParse(id, out assetID)) | 140 | AssetBase asset = Get(id); |
127 | return null; | ||
128 | 141 | ||
129 | AssetBase asset = m_Database.GetAsset(assetID); | ||
130 | if (asset != null) | 142 | if (asset != null) |
131 | return asset.Metadata; | 143 | return asset.Metadata; |
132 | 144 | else | |
133 | return null; | 145 | return null; |
134 | } | 146 | } |
135 | 147 | ||
136 | public virtual byte[] GetData(string id) | 148 | public virtual byte[] GetData(string id) |
137 | { | 149 | { |
138 | // m_log.DebugFormat("[XASSET SERVICE]: Get asset data for {0}", id); | 150 | // m_log.DebugFormat("[XASSET SERVICE]: Get asset data for {0}", id); |
139 | 151 | ||
140 | UUID assetID; | 152 | AssetBase asset = Get(id); |
141 | 153 | ||
142 | if (!UUID.TryParse(id, out assetID)) | 154 | if (asset != null) |
155 | return asset.Data; | ||
156 | else | ||
143 | return null; | 157 | return null; |
144 | |||
145 | AssetBase asset = m_Database.GetAsset(assetID); | ||
146 | return asset.Data; | ||
147 | } | 158 | } |
148 | 159 | ||
149 | public virtual bool Get(string id, Object sender, AssetRetrieved handler) | 160 | public virtual bool Get(string id, Object sender, AssetRetrieved handler) |
@@ -155,7 +166,7 @@ namespace OpenSim.Services.AssetService | |||
155 | if (!UUID.TryParse(id, out assetID)) | 166 | if (!UUID.TryParse(id, out assetID)) |
156 | return false; | 167 | return false; |
157 | 168 | ||
158 | AssetBase asset = m_Database.GetAsset(assetID); | 169 | AssetBase asset = Get(id); |
159 | 170 | ||
160 | //m_log.DebugFormat("[XASSET SERVICE]: Got asset {0}", asset); | 171 | //m_log.DebugFormat("[XASSET SERVICE]: Got asset {0}", asset); |
161 | 172 | ||
@@ -194,7 +205,15 @@ namespace OpenSim.Services.AssetService | |||
194 | if (!UUID.TryParse(id, out assetID)) | 205 | if (!UUID.TryParse(id, out assetID)) |
195 | return false; | 206 | return false; |
196 | 207 | ||
208 | // Don't bother deleting from a chained asset service. This isn't a big deal since deleting happens | ||
209 | // very rarely. | ||
210 | |||
197 | return m_Database.Delete(id); | 211 | return m_Database.Delete(id); |
198 | } | 212 | } |
213 | |||
214 | private void MigrateFromChainedService(AssetBase asset) | ||
215 | { | ||
216 | Util.FireAndForget(o => { Store(asset); m_ChainedAssetService.Delete(asset.ID); }); | ||
217 | } | ||
199 | } | 218 | } |
200 | } \ No newline at end of file | 219 | } \ No newline at end of file |
diff --git a/OpenSim/Services/AssetService/XAssetServiceBase.cs b/OpenSim/Services/AssetService/XAssetServiceBase.cs index 0c5c2c3..c118c9d 100644 --- a/OpenSim/Services/AssetService/XAssetServiceBase.cs +++ b/OpenSim/Services/AssetService/XAssetServiceBase.cs | |||
@@ -27,9 +27,11 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Reflection; | 29 | using System.Reflection; |
30 | using log4net; | ||
30 | using Nini.Config; | 31 | using Nini.Config; |
31 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
32 | using OpenSim.Data; | 33 | using OpenSim.Data; |
34 | using OpenSim.Server.Base; | ||
33 | using OpenSim.Services.Interfaces; | 35 | using OpenSim.Services.Interfaces; |
34 | using OpenSim.Services.Base; | 36 | using OpenSim.Services.Base; |
35 | 37 | ||
@@ -37,10 +39,15 @@ namespace OpenSim.Services.AssetService | |||
37 | { | 39 | { |
38 | public class XAssetServiceBase : ServiceBase | 40 | public class XAssetServiceBase : ServiceBase |
39 | { | 41 | { |
40 | protected IXAssetDataPlugin m_Database = null; | 42 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
41 | protected IAssetLoader m_AssetLoader = null; | ||
42 | 43 | ||
43 | public XAssetServiceBase(IConfigSource config) : base(config) | 44 | protected IXAssetDataPlugin m_Database; |
45 | protected IAssetLoader m_AssetLoader; | ||
46 | protected IAssetService m_ChainedAssetService; | ||
47 | |||
48 | protected bool HasChainedAssetService { get { return m_ChainedAssetService != null; } } | ||
49 | |||
50 | public XAssetServiceBase(IConfigSource config, string configName) : base(config) | ||
44 | { | 51 | { |
45 | string dllName = String.Empty; | 52 | string dllName = String.Empty; |
46 | string connString = String.Empty; | 53 | string connString = String.Empty; |
@@ -48,7 +55,7 @@ namespace OpenSim.Services.AssetService | |||
48 | // | 55 | // |
49 | // Try reading the [AssetService] section first, if it exists | 56 | // Try reading the [AssetService] section first, if it exists |
50 | // | 57 | // |
51 | IConfig assetConfig = config.Configs["AssetService"]; | 58 | IConfig assetConfig = config.Configs[configName]; |
52 | if (assetConfig != null) | 59 | if (assetConfig != null) |
53 | { | 60 | { |
54 | dllName = assetConfig.GetString("StorageProvider", dllName); | 61 | dllName = assetConfig.GetString("StorageProvider", dllName); |
@@ -77,17 +84,35 @@ namespace OpenSim.Services.AssetService | |||
77 | if (m_Database == null) | 84 | if (m_Database == null) |
78 | throw new Exception("Could not find a storage interface in the given module"); | 85 | throw new Exception("Could not find a storage interface in the given module"); |
79 | 86 | ||
80 | m_Database.Initialise(connString); | 87 | string chainedAssetServiceDesignator = assetConfig.GetString("ChainedServiceModule", null); |
88 | |||
89 | if (chainedAssetServiceDesignator != null) | ||
90 | { | ||
91 | m_log.InfoFormat( | ||
92 | "[XASSET SERVICE BASE]: Loading chained asset service from {0}", chainedAssetServiceDesignator); | ||
81 | 93 | ||
82 | string loaderName = assetConfig.GetString("DefaultAssetLoader", | 94 | Object[] args = new Object[] { config, configName }; |
83 | String.Empty); | 95 | m_ChainedAssetService = ServerUtils.LoadPlugin<IAssetService>(chainedAssetServiceDesignator, args); |
84 | 96 | ||
85 | if (loaderName != String.Empty) | 97 | if (!HasChainedAssetService) |
98 | throw new Exception( | ||
99 | String.Format("Failed to load ChainedAssetService from {0}", chainedAssetServiceDesignator)); | ||
100 | } | ||
101 | |||
102 | m_Database.Initialise(connString); | ||
103 | |||
104 | if (HasChainedAssetService) | ||
86 | { | 105 | { |
87 | m_AssetLoader = LoadPlugin<IAssetLoader>(loaderName); | 106 | string loaderName = assetConfig.GetString("DefaultAssetLoader", |
107 | String.Empty); | ||
108 | |||
109 | if (loaderName != String.Empty) | ||
110 | { | ||
111 | m_AssetLoader = LoadPlugin<IAssetLoader>(loaderName); | ||
88 | 112 | ||
89 | if (m_AssetLoader == null) | 113 | if (m_AssetLoader == null) |
90 | throw new Exception("Asset loader could not be loaded"); | 114 | throw new Exception("Asset loader could not be loaded"); |
115 | } | ||
91 | } | 116 | } |
92 | } | 117 | } |
93 | } | 118 | } |
diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 182f4d9..a448cc5 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs | |||
@@ -60,6 +60,8 @@ namespace OpenSim.Tests.Common.Mock | |||
60 | public List<ImagePacketPacket> SentImagePacketPackets { get; private set; } | 60 | public List<ImagePacketPacket> SentImagePacketPackets { get; private set; } |
61 | public List<ImageNotInDatabasePacket> SentImageNotInDatabasePackets { get; private set; } | 61 | public List<ImageNotInDatabasePacket> SentImageNotInDatabasePackets { get; private set; } |
62 | 62 | ||
63 | public event Action<RegionInfo, Vector3, Vector3> OnReceivedMoveAgentIntoRegion; | ||
64 | |||
63 | // disable warning: public events, part of the public API | 65 | // disable warning: public events, part of the public API |
64 | #pragma warning disable 67 | 66 | #pragma warning disable 67 |
65 | 67 | ||
@@ -566,6 +568,8 @@ namespace OpenSim.Tests.Common.Mock | |||
566 | 568 | ||
567 | public virtual void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) | 569 | public virtual void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) |
568 | { | 570 | { |
571 | if (OnReceivedMoveAgentIntoRegion != null) | ||
572 | OnReceivedMoveAgentIntoRegion(regInfo, pos, look); | ||
569 | } | 573 | } |
570 | 574 | ||
571 | public virtual AgentCircuitData RequestClientInfo() | 575 | public virtual AgentCircuitData RequestClientInfo() |
diff --git a/OpenSim/Tests/Common/Mock/TestEventQueueGetModule.cs b/OpenSim/Tests/Common/Mock/TestEventQueueGetModule.cs new file mode 100644 index 0000000..6707019 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestEventQueueGetModule.cs | |||
@@ -0,0 +1,178 @@ | |||
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.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Net; | ||
32 | using System.Reflection; | ||
33 | using System.Threading; | ||
34 | using log4net; | ||
35 | using Nini.Config; | ||
36 | using Mono.Addins; | ||
37 | using OpenMetaverse; | ||
38 | using OpenMetaverse.StructuredData; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Framework.Servers; | ||
41 | using OpenSim.Region.Framework.Interfaces; | ||
42 | using OpenSim.Region.Framework.Scenes; | ||
43 | |||
44 | namespace OpenSim.Tests.Common | ||
45 | { | ||
46 | public class TestEventQueueGetModule : IEventQueue, INonSharedRegionModule | ||
47 | { | ||
48 | public class Event | ||
49 | { | ||
50 | public string Name { get; set; } | ||
51 | public object[] Args { get; set; } | ||
52 | |||
53 | public Event(string name, object[] args) | ||
54 | { | ||
55 | name = Name; | ||
56 | args = Args; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | public Dictionary<UUID, List<Event>> Events { get; set; } | ||
61 | |||
62 | public void Initialise(IConfigSource source) {} | ||
63 | |||
64 | public void Close() {} | ||
65 | |||
66 | public void AddRegion(Scene scene) | ||
67 | { | ||
68 | Events = new Dictionary<UUID, List<Event>>(); | ||
69 | scene.RegisterModuleInterface<IEventQueue>(this); | ||
70 | } | ||
71 | |||
72 | public void RemoveRegion (Scene scene) {} | ||
73 | |||
74 | public void RegionLoaded (Scene scene) {} | ||
75 | |||
76 | public string Name { get { return "TestEventQueueGetModule"; } } | ||
77 | |||
78 | public Type ReplaceableInterface { get { return null; } } | ||
79 | |||
80 | private void AddEvent(UUID avatarID, string name, params object[] args) | ||
81 | { | ||
82 | Console.WriteLine("Adding event {0} for {1}", name, avatarID); | ||
83 | |||
84 | List<Event> avEvents; | ||
85 | |||
86 | if (!Events.ContainsKey(avatarID)) | ||
87 | { | ||
88 | avEvents = new List<Event>(); | ||
89 | Events[avatarID] = avEvents; | ||
90 | } | ||
91 | else | ||
92 | { | ||
93 | avEvents = Events[avatarID]; | ||
94 | } | ||
95 | |||
96 | avEvents.Add(new Event(name, args)); | ||
97 | } | ||
98 | |||
99 | public void ClearEvents() | ||
100 | { | ||
101 | if (Events != null) | ||
102 | Events.Clear(); | ||
103 | } | ||
104 | |||
105 | public bool Enqueue(OSD o, UUID avatarID) | ||
106 | { | ||
107 | AddEvent(avatarID, "Enqueue", o); | ||
108 | return true; | ||
109 | } | ||
110 | |||
111 | public void DisableSimulator(ulong handle, UUID avatarID) | ||
112 | { | ||
113 | AddEvent(avatarID, "DisableSimulator", handle); | ||
114 | } | ||
115 | |||
116 | public void EnableSimulator (ulong handle, IPEndPoint endPoint, UUID avatarID) | ||
117 | { | ||
118 | AddEvent(avatarID, "EnableSimulator", handle); | ||
119 | } | ||
120 | |||
121 | public void EstablishAgentCommunication (UUID avatarID, IPEndPoint endPoint, string capsPath) | ||
122 | { | ||
123 | AddEvent(avatarID, "EstablishAgentCommunication", endPoint, capsPath); | ||
124 | } | ||
125 | |||
126 | public void TeleportFinishEvent (ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, uint locationID, uint flags, string capsURL, UUID agentID) | ||
127 | { | ||
128 | AddEvent(agentID, "TeleportFinishEvent", regionHandle, simAccess, regionExternalEndPoint, locationID, flags, capsURL); | ||
129 | } | ||
130 | |||
131 | public void CrossRegion (ulong handle, Vector3 pos, Vector3 lookAt, IPEndPoint newRegionExternalEndPoint, string capsURL, UUID avatarID, UUID sessionID) | ||
132 | { | ||
133 | AddEvent(avatarID, "CrossRegion", handle, pos, lookAt, newRegionExternalEndPoint, capsURL, sessionID); | ||
134 | } | ||
135 | |||
136 | public void ChatterboxInvitation( | ||
137 | UUID sessionID, string sessionName, UUID fromAgent, string message, UUID toAgent, string fromName, | ||
138 | byte dialog, uint timeStamp, bool offline, int parentEstateID, Vector3 position, uint ttl, | ||
139 | UUID transactionID, bool fromGroup, byte[] binaryBucket) | ||
140 | { | ||
141 | AddEvent( | ||
142 | toAgent, "ChatterboxInvitation", sessionID, sessionName, fromAgent, message, toAgent, fromName, dialog, | ||
143 | timeStamp, offline, parentEstateID, position, ttl, transactionID, fromGroup, binaryBucket); | ||
144 | } | ||
145 | |||
146 | public void ChatterBoxSessionAgentListUpdates (UUID sessionID, UUID fromAgent, UUID toAgent, bool canVoiceChat, bool isModerator, bool textMute) | ||
147 | { | ||
148 | AddEvent(toAgent, "ChatterBoxSessionAgentListUpdates", sessionID, fromAgent, canVoiceChat, isModerator, textMute); | ||
149 | } | ||
150 | |||
151 | public void ParcelProperties (OpenMetaverse.Messages.Linden.ParcelPropertiesMessage parcelPropertiesMessage, UUID avatarID) | ||
152 | { | ||
153 | AddEvent(avatarID, "ParcelProperties", parcelPropertiesMessage); | ||
154 | } | ||
155 | |||
156 | public void GroupMembership (OpenMetaverse.Packets.AgentGroupDataUpdatePacket groupUpdate, UUID avatarID) | ||
157 | { | ||
158 | AddEvent(avatarID, "GroupMembership", groupUpdate); | ||
159 | } | ||
160 | |||
161 | public OSD ScriptRunningEvent (UUID objectID, UUID itemID, bool running, bool mono) | ||
162 | { | ||
163 | Console.WriteLine("ONE"); | ||
164 | throw new System.NotImplementedException (); | ||
165 | } | ||
166 | |||
167 | public OSD BuildEvent (string eventName, OSD eventBody) | ||
168 | { | ||
169 | Console.WriteLine("TWO"); | ||
170 | throw new System.NotImplementedException (); | ||
171 | } | ||
172 | |||
173 | public void partPhysicsProperties (uint localID, byte physhapetype, float density, float friction, float bounce, float gravmod, UUID avatarID) | ||
174 | { | ||
175 | AddEvent(avatarID, "partPhysicsProperties", localID, physhapetype, density, friction, bounce, gravmod); | ||
176 | } | ||
177 | } | ||
178 | } \ No newline at end of file | ||
diff --git a/OpenSim/Tests/Common/Mock/TestLandChannel.cs b/OpenSim/Tests/Common/Mock/TestLandChannel.cs index 4b4d52d..3115035 100644 --- a/OpenSim/Tests/Common/Mock/TestLandChannel.cs +++ b/OpenSim/Tests/Common/Mock/TestLandChannel.cs | |||
@@ -81,6 +81,11 @@ namespace OpenSim.Tests.Common.Mock | |||
81 | return obj; | 81 | return obj; |
82 | } | 82 | } |
83 | 83 | ||
84 | public ILandObject GetLandObject(Vector3 position) | ||
85 | { | ||
86 | return GetLandObject(position.X, position.Y); | ||
87 | } | ||
88 | |||
84 | public ILandObject GetLandObject(int x, int y) | 89 | public ILandObject GetLandObject(int x, int y) |
85 | { | 90 | { |
86 | return GetNoLand(); | 91 | return GetNoLand(); |
diff --git a/bin/HttpServer_OpenSim.dll b/bin/HttpServer_OpenSim.dll index fd7ad74..e15493d 100755 --- a/bin/HttpServer_OpenSim.dll +++ b/bin/HttpServer_OpenSim.dll | |||
Binary files differ | |||
diff --git a/bin/HttpServer_OpenSim.pdb b/bin/HttpServer_OpenSim.pdb index f56e891..cfff9a7 100644 --- a/bin/HttpServer_OpenSim.pdb +++ b/bin/HttpServer_OpenSim.pdb | |||
Binary files differ | |||
diff --git a/bin/HttpServer_OpenSim.xml b/bin/HttpServer_OpenSim.xml index fa88fc7..61c3ad8 100644 --- a/bin/HttpServer_OpenSim.xml +++ b/bin/HttpServer_OpenSim.xml | |||
@@ -1669,6 +1669,65 @@ | |||
1669 | A header have been received. | 1669 | A header have been received. |
1670 | </summary> | 1670 | </summary> |
1671 | </member> | 1671 | </member> |
1672 | <member name="T:HttpServer.LocklessQueue`1"> | ||
1673 | <summary> | ||
1674 | A thread-safe lockless queue that supports multiple readers and | ||
1675 | multiple writers | ||
1676 | </summary> | ||
1677 | </member> | ||
1678 | <member name="F:HttpServer.LocklessQueue`1.head"> | ||
1679 | <summary>Queue head</summary> | ||
1680 | </member> | ||
1681 | <member name="F:HttpServer.LocklessQueue`1.tail"> | ||
1682 | <summary>Queue tail</summary> | ||
1683 | </member> | ||
1684 | <member name="F:HttpServer.LocklessQueue`1.count"> | ||
1685 | <summary>Queue item count</summary> | ||
1686 | </member> | ||
1687 | <member name="M:HttpServer.LocklessQueue`1.#ctor"> | ||
1688 | <summary> | ||
1689 | Constructor | ||
1690 | </summary> | ||
1691 | </member> | ||
1692 | <member name="M:HttpServer.LocklessQueue`1.Enqueue(`0)"> | ||
1693 | <summary> | ||
1694 | Enqueue an item | ||
1695 | </summary> | ||
1696 | <param name="item">Item to enqeue</param> | ||
1697 | </member> | ||
1698 | <member name="M:HttpServer.LocklessQueue`1.TryDequeue(`0@)"> | ||
1699 | <summary> | ||
1700 | Try to dequeue an item | ||
1701 | </summary> | ||
1702 | <param name="item">Dequeued item if the dequeue was successful</param> | ||
1703 | <returns>True if an item was successfully deqeued, otherwise false</returns> | ||
1704 | </member> | ||
1705 | <member name="P:HttpServer.LocklessQueue`1.Count"> | ||
1706 | <summary>Gets the current number of items in the queue. Since this | ||
1707 | is a lockless collection this value should be treated as a close | ||
1708 | estimate</summary> | ||
1709 | </member> | ||
1710 | <member name="T:HttpServer.LocklessQueue`1.SingleLinkNode"> | ||
1711 | <summary> | ||
1712 | Provides a node container for data in a singly linked list | ||
1713 | </summary> | ||
1714 | </member> | ||
1715 | <member name="F:HttpServer.LocklessQueue`1.SingleLinkNode.Next"> | ||
1716 | <summary>Pointer to the next node in list</summary> | ||
1717 | </member> | ||
1718 | <member name="F:HttpServer.LocklessQueue`1.SingleLinkNode.Item"> | ||
1719 | <summary>The data contained by the node</summary> | ||
1720 | </member> | ||
1721 | <member name="M:HttpServer.LocklessQueue`1.SingleLinkNode.#ctor"> | ||
1722 | <summary> | ||
1723 | Constructor | ||
1724 | </summary> | ||
1725 | </member> | ||
1726 | <member name="M:HttpServer.LocklessQueue`1.SingleLinkNode.#ctor(`0)"> | ||
1727 | <summary> | ||
1728 | Constructor | ||
1729 | </summary> | ||
1730 | </member> | ||
1672 | <member name="T:HttpServer.IHttpRequest"> | 1731 | <member name="T:HttpServer.IHttpRequest"> |
1673 | <summary> | 1732 | <summary> |
1674 | Contains server side HTTP request information. | 1733 | Contains server side HTTP request information. |
@@ -2825,6 +2884,11 @@ | |||
2825 | <param name="protocol">Kind of HTTPS protocol. Usually TLS or SSL.</param> | 2884 | <param name="protocol">Kind of HTTPS protocol. Usually TLS or SSL.</param> |
2826 | <returns>A created <see cref="T:HttpServer.IHttpClientContext"/>.</returns> | 2885 | <returns>A created <see cref="T:HttpServer.IHttpClientContext"/>.</returns> |
2827 | </member> | 2886 | </member> |
2887 | <member name="M:HttpServer.IHttpContextFactory.Shutdown"> | ||
2888 | <summary> | ||
2889 | Server is shutting down so shut down the factory | ||
2890 | </summary> | ||
2891 | </member> | ||
2828 | <member name="E:HttpServer.IHttpContextFactory.RequestReceived"> | 2892 | <member name="E:HttpServer.IHttpContextFactory.RequestReceived"> |
2829 | <summary> | 2893 | <summary> |
2830 | A request have been received from one of the contexts. | 2894 | A request have been received from one of the contexts. |
@@ -2876,6 +2940,11 @@ | |||
2876 | A creates <see cref="T:HttpServer.IHttpClientContext"/>. | 2940 | A creates <see cref="T:HttpServer.IHttpClientContext"/>. |
2877 | </returns> | 2941 | </returns> |
2878 | </member> | 2942 | </member> |
2943 | <member name="M:HttpServer.HttpContextFactory.Shutdown"> | ||
2944 | <summary> | ||
2945 | Server is shutting down so shut down the factory | ||
2946 | </summary> | ||
2947 | </member> | ||
2879 | <member name="P:HttpServer.HttpContextFactory.UseTraceLogs"> | 2948 | <member name="P:HttpServer.HttpContextFactory.UseTraceLogs"> |
2880 | <summary> | 2949 | <summary> |
2881 | True if detailed trace logs should be written. | 2950 | True if detailed trace logs should be written. |
@@ -4315,6 +4384,58 @@ | |||
4315 | </summary> | 4384 | </summary> |
4316 | <param name="message">message describing the error</param> | 4385 | <param name="message">message describing the error</param> |
4317 | </member> | 4386 | </member> |
4387 | <member name="T:HttpServer.ContextTimeoutManager"> | ||
4388 | <summary> | ||
4389 | Timeout Manager. Checks for dead clients. Clients with open connections that are not doing anything. Closes sessions opened with keepalive. | ||
4390 | </summary> | ||
4391 | </member> | ||
4392 | <member name="M:HttpServer.ContextTimeoutManager.ProcessContextTimeouts"> | ||
4393 | <summary> | ||
4394 | Causes the watcher to immediately check the connections. | ||
4395 | </summary> | ||
4396 | </member> | ||
4397 | <member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCount"> | ||
4398 | <summary> | ||
4399 | Environment.TickCount is an int but it counts all 32 bits so it goes positive | ||
4400 | and negative every 24.9 days. This trims down TickCount so it doesn't wrap | ||
4401 | for the callers. | ||
4402 | This trims it to a 12 day interval so don't let your frame time get too long. | ||
4403 | </summary> | ||
4404 | <returns></returns> | ||
4405 | </member> | ||
4406 | <member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCountSubtract(System.Int32,System.Int32)"> | ||
4407 | <summary> | ||
4408 | Environment.TickCount is an int but it counts all 32 bits so it goes positive | ||
4409 | and negative every 24.9 days. Subtracts the passed value (previously fetched by | ||
4410 | 'EnvironmentTickCount()') and accounts for any wrapping. | ||
4411 | </summary> | ||
4412 | <param name="newValue"></param> | ||
4413 | <param name="prevValue"></param> | ||
4414 | <returns>subtraction of passed prevValue from current Environment.TickCount</returns> | ||
4415 | </member> | ||
4416 | <member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCountAdd(System.Int32,System.Int32)"> | ||
4417 | <summary> | ||
4418 | Environment.TickCount is an int but it counts all 32 bits so it goes positive | ||
4419 | and negative every 24.9 days. Subtracts the passed value (previously fetched by | ||
4420 | 'EnvironmentTickCount()') and accounts for any wrapping. | ||
4421 | </summary> | ||
4422 | <param name="newValue"></param> | ||
4423 | <param name="prevValue"></param> | ||
4424 | <returns>subtraction of passed prevValue from current Environment.TickCount</returns> | ||
4425 | </member> | ||
4426 | <member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCountSubtract(System.Int32)"> | ||
4427 | <summary> | ||
4428 | Environment.TickCount is an int but it counts all 32 bits so it goes positive | ||
4429 | and negative every 24.9 days. Subtracts the passed value (previously fetched by | ||
4430 | 'EnvironmentTickCount()') and accounts for any wrapping. | ||
4431 | </summary> | ||
4432 | <returns>subtraction of passed prevValue from current Environment.TickCount</returns> | ||
4433 | </member> | ||
4434 | <member name="T:HttpServer.ContextTimeoutManager.MonitorType"> | ||
4435 | <summary> | ||
4436 | Use a Thread or a Timer to monitor the ugly | ||
4437 | </summary> | ||
4438 | </member> | ||
4318 | <member name="T:HttpServer.Sessions.MemorySessionStore"> | 4439 | <member name="T:HttpServer.Sessions.MemorySessionStore"> |
4319 | <summary> | 4440 | <summary> |
4320 | Session store using memory for each session. | 4441 | Session store using memory for each session. |
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index eab1fce..ce2e600 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example | |||
@@ -210,13 +210,12 @@ | |||
210 | ;; Choose one of the physics engines below | 210 | ;; Choose one of the physics engines below |
211 | ;# {physics} {} {Select physics engine} {OpenDynamicsEngine BulletSim basicphysics POS} OpenDynamicsEngine | 211 | ;# {physics} {} {Select physics engine} {OpenDynamicsEngine BulletSim basicphysics POS} OpenDynamicsEngine |
212 | ;; OpenDynamicsEngine is by some distance the most developed physics engine | 212 | ;; OpenDynamicsEngine is by some distance the most developed physics engine |
213 | ;; BulletSim is incomplete and experimental but in active development. BulletSimN is a purely C# version of BulletSim. | 213 | ;; BulletSim is experimental and in active development. |
214 | ;; basicphysics effectively does not model physics at all, making all | 214 | ;; basicphysics effectively does not model physics at all, making all |
215 | ;; objects phantom. | 215 | ;; objects phantom. |
216 | ;; Default is OpenDynamicsEngine | 216 | ;; Default is OpenDynamicsEngine |
217 | ; physics = OpenDynamicsEngine | 217 | ; physics = OpenDynamicsEngine |
218 | ; physics = BulletSim | 218 | ; physics = BulletSim |
219 | ; physics = BulletSimN | ||
220 | ; physics = basicphysics | 219 | ; physics = basicphysics |
221 | ; physics = POS | 220 | ; physics = POS |
222 | 221 | ||
@@ -542,6 +541,13 @@ | |||
542 | ; shout_distance = 100 | 541 | ; shout_distance = 100 |
543 | 542 | ||
544 | 543 | ||
544 | [EntityTransfer] | ||
545 | ;# {DisableInterRegionTeleportCancellation} {} {Determine whether the cancel button is shown at all during teleports.} {false true} false | ||
546 | ;; This option exists because cancelling at certain points can result in an unuseable session (frozen avatar, etc.) | ||
547 | ;; Disabling cancellation can be okay in small closed grids where all teleports are highly likely to suceed. | ||
548 | ;DisableInterRegionTeleportCancellation = false | ||
549 | |||
550 | |||
545 | [Messaging] | 551 | [Messaging] |
546 | ;# {OfflineMessageModule} {} {Module to use for offline message storage} {OfflineMessageModule "Offline Message Module V2" *} | 552 | ;# {OfflineMessageModule} {} {Module to use for offline message storage} {OfflineMessageModule "Offline Message Module V2" *} |
547 | ;; Module to handle offline messaging. The core module requires an external | 553 | ;; Module to handle offline messaging. The core module requires an external |
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 417150a..1d2c0cf 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini | |||
@@ -628,6 +628,11 @@ | |||
628 | ; Minimum user level required for HyperGrid teleports | 628 | ; Minimum user level required for HyperGrid teleports |
629 | LevelHGTeleport = 0 | 629 | LevelHGTeleport = 0 |
630 | 630 | ||
631 | ; Determine whether the cancel button is shown at all during teleports. | ||
632 | ; This option exists because cancelling at certain points can result in an unuseable session (frozen avatar, etc.) | ||
633 | ; Disabling cancellation can be okay in small closed grids where all teleports are highly likely to suceed. | ||
634 | DisableInterRegionTeleportCancellation = false | ||
635 | |||
631 | 636 | ||
632 | [Messaging] | 637 | [Messaging] |
633 | ; Control which region module is used for instant messaging. | 638 | ; Control which region module is used for instant messaging. |
diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index 7746ebc..581c31d 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example | |||
@@ -26,12 +26,12 @@ | |||
26 | ; Set path to directory for plugin registry. Information | 26 | ; Set path to directory for plugin registry. Information |
27 | ; about the registered repositories and installed plugins | 27 | ; about the registered repositories and installed plugins |
28 | ; will be stored here | 28 | ; will be stored here |
29 | ; The Robust.exe process must hvae R/W access to the location | 29 | ; The Robust.exe process must have R/W access to the location |
30 | RegistryLocation = "." | 30 | RegistryLocation = "." |
31 | 31 | ||
32 | ; Modular configurations | 32 | ; Modular configurations |
33 | ; Set path to directory for modular ini files... | 33 | ; Set path to directory for modular ini files... |
34 | ; The Robust.exe process must hvae R/W access to the location | 34 | ; The Robust.exe process must have R/W access to the location |
35 | ConfigDirectory = "/home/opensim/etc/Configs" | 35 | ConfigDirectory = "/home/opensim/etc/Configs" |
36 | 36 | ||
37 | [ServiceList] | 37 | [ServiceList] |
@@ -171,7 +171,7 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset | |||
171 | ;; Allow Hyperlinks to be created at the console | 171 | ;; Allow Hyperlinks to be created at the console |
172 | HypergridLinker = true | 172 | HypergridLinker = true |
173 | 173 | ||
174 | ;; If you have this set under [Startup], no need to set it here, leave it commented | 174 | ;; If you have this set under [Hypergrid], no need to set it here, leave it commented |
175 | ; GatekeeperURI = "http://127.0.0.1:8002" | 175 | ; GatekeeperURI = "http://127.0.0.1:8002" |
176 | 176 | ||
177 | 177 | ||
@@ -326,7 +326,7 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset | |||
326 | ; HasProxy = false | 326 | ; HasProxy = false |
327 | 327 | ||
328 | ; Defaults for the users, if none is specified in the useraccounts table entry (ServiceURLs) | 328 | ; Defaults for the users, if none is specified in the useraccounts table entry (ServiceURLs) |
329 | ;; If you have Gatekeeper set under [Startup], no need to set it here, leave it commented | 329 | ;; If you have GatekeeperURI set under [Hypergrid], no need to set it here, leave it commented |
330 | ; GatekeeperURI = "http://127.0.0.1:8002" | 330 | ; GatekeeperURI = "http://127.0.0.1:8002" |
331 | 331 | ||
332 | SRV_HomeURI = "http://127.0.0.1:8002" | 332 | SRV_HomeURI = "http://127.0.0.1:8002" |
@@ -436,7 +436,7 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset | |||
436 | AuthenticationService = "OpenSim.Services.Connectors.dll:AuthenticationServicesConnector" | 436 | AuthenticationService = "OpenSim.Services.Connectors.dll:AuthenticationServicesConnector" |
437 | SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" | 437 | SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" |
438 | ; how does the outside world reach me? This acts as public key too. | 438 | ; how does the outside world reach me? This acts as public key too. |
439 | ;; If you have GatekeeperURI set under [Startup], no need to set it here, leave it commented | 439 | ;; If you have GatekeeperURI set under [Hypergrid], no need to set it here, leave it commented |
440 | ; ExternalName = "http://127.0.0.1:8002" | 440 | ; ExternalName = "http://127.0.0.1:8002" |
441 | 441 | ||
442 | ; Does this grid allow incoming links to any region in it? | 442 | ; Does this grid allow incoming links to any region in it? |
@@ -531,7 +531,7 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset | |||
531 | UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" | 531 | UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" |
532 | AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" | 532 | AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" |
533 | 533 | ||
534 | ;; Can overwrite the default in [Startup], but probably shouldn't | 534 | ;; Can overwrite the default in [Hypergrid], but probably shouldn't |
535 | ; HomeURI = "http://127.0.0.1:8002" | 535 | ; HomeURI = "http://127.0.0.1:8002" |
536 | 536 | ||
537 | ; * The interface that local users get when they are in other grids. | 537 | ; * The interface that local users get when they are in other grids. |
@@ -542,7 +542,7 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset | |||
542 | LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGAssetService" | 542 | LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGAssetService" |
543 | UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" | 543 | UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" |
544 | 544 | ||
545 | ;; Can overwrite the default in [Startup], but probably shouldn't | 545 | ;; Can overwrite the default in [Hypergrid], but probably shouldn't |
546 | ; HomeURI = "http://127.0.0.1:8002" | 546 | ; HomeURI = "http://127.0.0.1:8002" |
547 | 547 | ||
548 | ;; The asset types that this grid can export to / import from other grids. | 548 | ;; The asset types that this grid can export to / import from other grids. |
@@ -583,7 +583,7 @@ HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:Asset | |||
583 | UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" | 583 | UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" |
584 | 584 | ||
585 | ;; What is the HomeURI of users associated with this grid? | 585 | ;; What is the HomeURI of users associated with this grid? |
586 | ;; Can overwrite the default in [Startup], but probably shouldn't | 586 | ;; Can overwrite the default in [Hypergrid], but probably shouldn't |
587 | ; HomeURI = "http://127.0.0.1:8002" | 587 | ; HomeURI = "http://127.0.0.1:8002" |
588 | 588 | ||
589 | 589 | ||
diff --git a/prebuild.xml b/prebuild.xml index 2b8e963..7a4455d 100644 --- a/prebuild.xml +++ b/prebuild.xml | |||
@@ -899,6 +899,7 @@ | |||
899 | <Reference name="OpenSim.Framework"/> | 899 | <Reference name="OpenSim.Framework"/> |
900 | <Reference name="OpenSim.Framework.Console"/> | 900 | <Reference name="OpenSim.Framework.Console"/> |
901 | <Reference name="OpenSim.Framework.Servers.HttpServer"/> | 901 | <Reference name="OpenSim.Framework.Servers.HttpServer"/> |
902 | <Reference name="OpenSim.Server.Base"/> | ||
902 | <Reference name="OpenSim.Services.Interfaces"/> | 903 | <Reference name="OpenSim.Services.Interfaces"/> |
903 | <Reference name="OpenSim.Services.Base"/> | 904 | <Reference name="OpenSim.Services.Base"/> |
904 | <Reference name="OpenSim.Services.Connectors"/> | 905 | <Reference name="OpenSim.Services.Connectors"/> |
@@ -1913,121 +1914,6 @@ | |||
1913 | </Files> | 1914 | </Files> |
1914 | </Project> | 1915 | </Project> |
1915 | 1916 | ||
1916 | <!-- REST plugins --> | ||
1917 | <Project frameworkVersion="v3_5" name="OpenSim.ApplicationPlugins.Rest" path="OpenSim/ApplicationPlugins/Rest" type="Library"> | ||
1918 | <Configuration name="Debug"> | ||
1919 | <Options> | ||
1920 | <OutputPath>../../../bin/</OutputPath> | ||
1921 | </Options> | ||
1922 | </Configuration> | ||
1923 | <Configuration name="Release"> | ||
1924 | <Options> | ||
1925 | <OutputPath>../../../bin/</OutputPath> | ||
1926 | </Options> | ||
1927 | </Configuration> | ||
1928 | |||
1929 | <ReferencePath>../../../bin/</ReferencePath> | ||
1930 | <Reference name="Mono.Addins" path="../../../bin/"/> | ||
1931 | <Reference name="System"/> | ||
1932 | <Reference name="System.Xml"/> | ||
1933 | <Reference name="OpenMetaverseTypes" path="../../../bin/"/> | ||
1934 | <Reference name="Nini" path="../../../bin/"/> | ||
1935 | <Reference name="XMLRPC" path="../../../bin/"/> | ||
1936 | <Reference name="OpenSim"/> | ||
1937 | <Reference name="OpenSim.Region.ClientStack"/> | ||
1938 | <Reference name="OpenSim.Region.Framework"/> | ||
1939 | <Reference name="OpenSim.Region.CoreModules"/> | ||
1940 | <Reference name="OpenSim.Framework.Communications"/> | ||
1941 | <Reference name="OpenSim.Framework"/> | ||
1942 | <Reference name="OpenSim.Framework.Servers"/> | ||
1943 | <Reference name="OpenSim.Framework.Servers.HttpServer"/> | ||
1944 | <Reference name="OpenSim.Framework.Console"/> | ||
1945 | <Reference name="log4net" path="../../../bin/"/> | ||
1946 | |||
1947 | <Files> | ||
1948 | <Match pattern="*.cs" recurse="false"/> | ||
1949 | </Files> | ||
1950 | </Project> | ||
1951 | |||
1952 | <Project frameworkVersion="v3_5" name="OpenSim.ApplicationPlugins.Rest.Regions" path="OpenSim/ApplicationPlugins/Rest/Regions" type="Library"> | ||
1953 | <Configuration name="Debug"> | ||
1954 | <Options> | ||
1955 | <OutputPath>../../../../bin/</OutputPath> | ||
1956 | </Options> | ||
1957 | </Configuration> | ||
1958 | <Configuration name="Release"> | ||
1959 | <Options> | ||
1960 | <OutputPath>../../../../bin/</OutputPath> | ||
1961 | </Options> | ||
1962 | </Configuration> | ||
1963 | |||
1964 | <ReferencePath>../../../../bin/</ReferencePath> | ||
1965 | <Reference name="Mono.Addins" path="../../../../bin/"/> | ||
1966 | <Reference name="System"/> | ||
1967 | <Reference name="System.Xml"/> | ||
1968 | <Reference name="OpenMetaverseTypes" path="../../../../bin/"/> | ||
1969 | <Reference name="Nini" path="../../../../bin/"/> | ||
1970 | <Reference name="XMLRPC" path="../../../../bin/"/> | ||
1971 | <Reference name="OpenSim"/> | ||
1972 | <Reference name="OpenSim.Region.Framework"/> | ||
1973 | <Reference name="OpenSim.Region.ClientStack"/> | ||
1974 | <Reference name="OpenSim.Region.CoreModules"/> | ||
1975 | <Reference name="OpenSim.Framework.Communications"/> | ||
1976 | <Reference name="OpenSim.Framework"/> | ||
1977 | <Reference name="OpenSim.Framework.Servers"/> | ||
1978 | <Reference name="OpenSim.Framework.Servers.HttpServer"/> | ||
1979 | <Reference name="OpenSim.Framework.Console"/> | ||
1980 | <Reference name="OpenSim.ApplicationPlugins.Rest"/> | ||
1981 | <Reference name="log4net" path="../../../../bin/"/> | ||
1982 | |||
1983 | <Files> | ||
1984 | <Match pattern="*.cs" recurse="true"/> | ||
1985 | <Match buildAction="EmbeddedResource" path="Resources" pattern="*.addin.xml" recurse="true"/> | ||
1986 | </Files> | ||
1987 | </Project> | ||
1988 | |||
1989 | <Project frameworkVersion="v3_5" name="OpenSim.ApplicationPlugins.Rest.Inventory" path="OpenSim/ApplicationPlugins/Rest/Inventory" type="Library"> | ||
1990 | <Configuration name="Debug"> | ||
1991 | <Options> | ||
1992 | <OutputPath>../../../../bin/</OutputPath> | ||
1993 | </Options> | ||
1994 | </Configuration> | ||
1995 | <Configuration name="Release"> | ||
1996 | <Options> | ||
1997 | <OutputPath>../../../../bin/</OutputPath> | ||
1998 | </Options> | ||
1999 | </Configuration> | ||
2000 | |||
2001 | <ReferencePath>../../../../bin/</ReferencePath> | ||
2002 | <Reference name="Mono.Addins" path="../../../../bin/"/> | ||
2003 | <Reference name="System"/> | ||
2004 | <Reference name="System.Xml"/> | ||
2005 | <Reference name="System.Drawing"/> | ||
2006 | <Reference name="OpenMetaverseTypes" path="../../../../bin/"/> | ||
2007 | <Reference name="OpenMetaverse" path="../../../../bin/"/> | ||
2008 | <Reference name="Nini" path="../../../../bin/"/> | ||
2009 | <Reference name="XMLRPC" path="../../../../bin/"/> | ||
2010 | <Reference name="OpenSim"/> | ||
2011 | <Reference name="OpenSim.Region.Framework"/> | ||
2012 | <Reference name="OpenSim.Region.ClientStack"/> | ||
2013 | <Reference name="OpenSim.Region.CoreModules"/> | ||
2014 | <Reference name="OpenSim.Framework.Communications"/> | ||
2015 | <Reference name="OpenSim.Framework"/> | ||
2016 | <Reference name="OpenSim.Framework.Servers"/> | ||
2017 | <Reference name="OpenSim.Framework.Servers.HttpServer"/> | ||
2018 | <Reference name="OpenSim.Framework.Console"/> | ||
2019 | <Reference name="OpenSim.Services.Interfaces"/> | ||
2020 | <Reference name="OpenSim.ApplicationPlugins.Rest"/> | ||
2021 | <Reference name="log4net" path="../../../../bin/"/> | ||
2022 | |||
2023 | <Files> | ||
2024 | <Match pattern="*.cs" recurse="true"/> | ||
2025 | <Match buildAction="EmbeddedResource" path="Resources" pattern="*.addin.xml" recurse="true"/> | ||
2026 | </Files> | ||
2027 | </Project> | ||
2028 | |||
2029 | <!-- /REST plugins --> | ||
2030 | |||
2031 | <!-- Scene Server API Example Apps --> | 1917 | <!-- Scene Server API Example Apps --> |
2032 | 1918 | ||
2033 | <Project frameworkVersion="v3_5" name="OpenSim.Region.DataSnapshot" path="OpenSim/Region/DataSnapshot" type="Library"> | 1919 | <Project frameworkVersion="v3_5" name="OpenSim.Region.DataSnapshot" path="OpenSim/Region/DataSnapshot" type="Library"> |
@@ -2833,6 +2719,7 @@ | |||
2833 | <Reference name="Nini" path="../../../bin/"/> | 2719 | <Reference name="Nini" path="../../../bin/"/> |
2834 | <Reference name="nunit.framework" path="../../../bin/"/> | 2720 | <Reference name="nunit.framework" path="../../../bin/"/> |
2835 | <Reference name="OpenMetaverse" path="../../../bin/"/> | 2721 | <Reference name="OpenMetaverse" path="../../../bin/"/> |
2722 | <Reference name="OpenMetaverse.StructuredData" path="../../../bin/"/> | ||
2836 | <Reference name="OpenMetaverseTypes" path="../../../bin/"/> | 2723 | <Reference name="OpenMetaverseTypes" path="../../../bin/"/> |
2837 | <Reference name="OpenSim.Data"/> | 2724 | <Reference name="OpenSim.Data"/> |
2838 | <Reference name="OpenSim.Data.Null"/> | 2725 | <Reference name="OpenSim.Data.Null"/> |