aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorMelanie2013-03-18 23:31:27 +0000
committerMelanie2013-03-18 23:31:27 +0000
commit5e1f651e21ba81d8be9693d7e8a47d49daa9fce5 (patch)
tree4856d3aa25fcd942a26af39e1510f58fef3c934d /OpenSim
parentMerge commit 'ccd6f443e1092cb410f565e921f7cf4dd8cd2dac' into newmultiattach (diff)
parentImprove rejection of any attempt to reattach an object that is already attached. (diff)
downloadopensim-SC_OLD-5e1f651e21ba81d8be9693d7e8a47d49daa9fce5.zip
opensim-SC_OLD-5e1f651e21ba81d8be9693d7e8a47d49daa9fce5.tar.gz
opensim-SC_OLD-5e1f651e21ba81d8be9693d7e8a47d49daa9fce5.tar.bz2
opensim-SC_OLD-5e1f651e21ba81d8be9693d7e8a47d49daa9fce5.tar.xz
Merge branch 'master' into newmultiattach
Conflicts: OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/IRest.cs43
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/IRestHandler.cs59
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs1465
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/Resources/RestHandler.addin.xml11
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/Rest.cs551
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs860
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestAssetServices.cs383
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs448
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs662
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs2343
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestTestServices.cs246
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/tests/ITest.cs46
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/tests/Remote.cs204
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Regions/GETHandler.cs228
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Regions/GETRegionInfoHandler.cs136
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Regions/POSTHandler.cs122
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Regions/RegionDetails.cs98
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Regions/Resources/RestRegionPlugin.addin.xml11
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Regions/RestRegionPlugin.cs94
-rw-r--r--OpenSim/ApplicationPlugins/Rest/RestPlugin.cs417
-rw-r--r--OpenSim/ApplicationPlugins/Rest/rest.xsd276
-rw-r--r--OpenSim/Data/MySQL/MySQLXAssetData.cs169
-rw-r--r--OpenSim/Data/MySQL/Resources/XAssetStore.migrations30
-rw-r--r--OpenSim/Framework/DAMap.cs2
-rw-r--r--OpenSim/Framework/DOMap.cs (renamed from OpenSim/ApplicationPlugins/Rest/RestXmlWriter.cs)76
-rw-r--r--OpenSim/Framework/ILandChannel.cs7
-rw-r--r--OpenSim/Framework/PluginManager.cs4
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs12
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs23
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs2
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs20
-rw-r--r--OpenSim/Framework/Util.cs2
-rw-r--r--OpenSim/Region/Application/OpenSim.cs1
-rw-r--r--OpenSim/Region/Application/OpenSimBase.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs51
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs124
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs263
-rw-r--r--OpenSim/Region/CoreModules/Framework/DynamicAttributes/DAExampleModule.cs32
-rw-r--r--OpenSim/Region/CoreModules/Framework/DynamicAttributes/DOExampleModule.cs139
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs83
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs124
-rw-r--r--OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs2
-rw-r--r--OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs5
-rw-r--r--OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs9
-rw-r--r--OpenSim/Region/CoreModules/World/Land/LandChannel.cs5
-rw-r--r--OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs14
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectPart.cs38
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs77
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectUndoRedoTests.cs4
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs103
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs157
-rw-r--r--OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs41
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStore.cs39
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs36
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs39
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs50
-rw-r--r--OpenSim/Region/Physics/BasicPhysicsPlugin/BasicPhysicsScene.cs9
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs29
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs33
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSParam.cs12
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs100
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs6
-rw-r--r--OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs5
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs331
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs4
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs53
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs4
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs11
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiHttpTests.cs1
-rw-r--r--OpenSim/Server/Base/ServerUtils.cs20
-rw-r--r--OpenSim/Server/Base/ServicesServerBase.cs5
-rw-r--r--OpenSim/Server/ServerMain.cs2
-rw-r--r--OpenSim/Services/AssetService/AssetService.cs32
-rw-r--r--OpenSim/Services/AssetService/XAssetService.cs63
-rw-r--r--OpenSim/Services/AssetService/XAssetServiceBase.cs47
-rw-r--r--OpenSim/Tests/Common/Mock/TestClient.cs4
-rw-r--r--OpenSim/Tests/Common/Mock/TestEventQueueGetModule.cs178
-rw-r--r--OpenSim/Tests/Common/Mock/TestLandChannel.cs5
80 files changed, 1964 insertions, 9488 deletions
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
28namespace 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
28using OpenSim.Framework.Servers.HttpServer;
29
30namespace 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Security.Cryptography;
32using System.Text;
33using System.Text.RegularExpressions;
34using System.Xml;
35using OpenSim.Framework;
36using OpenSim.Framework.Servers;
37using OpenSim.Framework.Servers.HttpServer;
38using OpenSim.Services.Interfaces;
39
40using OpenMetaverse;
41
42namespace 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
29using System;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Text;
33using log4net;
34using Nini.Config;
35using OpenSim.Framework;
36using OpenSim.Framework.Communications;
37using OpenSim.Services.Interfaces;
38using IAvatarService = OpenSim.Services.Interfaces.IAvatarService;
39
40namespace 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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Xml;
32using OpenMetaverse;
33using OpenSim.Framework;
34using OpenSim.Framework.Servers;
35using OpenSim.Framework.Servers.HttpServer;
36using OpenSim.Services.Interfaces;
37
38namespace 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
28using System;
29using System.Xml;
30using OpenMetaverse;
31using OpenSim.Framework;
32using OpenSim.Framework.Servers;
33using OpenSim.Framework.Servers.HttpServer;
34
35namespace 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
28using System;
29using System.Xml;
30using System.IO;
31using OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Framework.Servers;
34using OpenSim.Framework.Servers.HttpServer;
35
36namespace 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
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using OpenSim.Framework.Servers;
32using OpenSim.Framework.Servers.HttpServer;
33
34namespace 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
28using System;
29using System.Collections.Generic;
30using System.Drawing;
31using System.Globalization;
32using System.IO;
33using System.Threading;
34using System.Timers;
35using System.Xml;
36using OpenMetaverse;
37using OpenMetaverse.Imaging;
38using OpenSim.Framework;
39
40using OpenSim.Framework.Servers;
41using OpenSim.Framework.Servers.HttpServer;
42using Timer=System.Timers.Timer;
43
44namespace 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
29using System;
30using System.Collections.Generic;
31using System.Reflection;
32using OpenSim.Framework.Servers;
33using OpenSim.Framework.Servers.HttpServer;
34
35namespace 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
29namespace 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
28using System;
29using OpenMetaverse;
30using OpenSim.Region.Framework.Scenes;
31
32namespace 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
28using System;
29using System.IO;
30using System.Xml.Serialization;
31using OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Framework.Servers;
34using OpenSim.Framework.Servers.HttpServer;
35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.Framework.Scenes;
37
38namespace 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
28using System;
29using System.IO;
30using System.Xml.Serialization;
31using OpenMetaverse;
32using OpenSim.Framework.Servers;
33using OpenSim.Framework.Servers.HttpServer;
34using OpenSim.Region.Framework.Interfaces;
35using OpenSim.Region.Framework.Scenes;
36
37namespace 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
28using System;
29using System.IO;
30using OpenMetaverse;
31using OpenSim.Framework.Servers;
32using OpenSim.Framework.Servers.HttpServer;
33using OpenSim.Region.Framework.Interfaces;
34using OpenSim.Region.Framework.Scenes;
35
36namespace 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
28using System;
29using System.Xml.Serialization;
30using OpenMetaverse;
31using OpenSim.Framework;
32
33namespace 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
28using System;
29using System.Xml.Serialization;
30
31namespace 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32using System.Xml;
33using log4net;
34using Nini.Config;
35using OpenMetaverse;
36using OpenSim.Framework;
37using OpenSim.Framework.Servers;
38using OpenSim.Framework.Servers.HttpServer;
39
40namespace 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
4BEGIN; 4BEGIN;
5 5
6CREATE TABLE `xassetsmeta` ( 6CREATE 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
21CREATE TABLE `xassetsdata` ( 21CREATE 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
28using System;
29using System.Collections;
30using System.Collections.Generic;
28using System.IO; 31using System.IO;
29using System.Text; 32using System.Text;
30using System.Xml; 33using System.Xml;
34using System.Xml.Schema;
35using System.Xml.Serialization;
36using OpenMetaverse;
37using OpenMetaverse.StructuredData;
31 38
32namespace OpenSim.ApplicationPlugins.Rest 39namespace 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;
39using OpenSim.Region.Framework.Interfaces; 39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes; 40using OpenSim.Region.Framework.Scenes;
41 41
42namespace OpenSim.Region.Framework.DynamicAttributes.DAExampleModule 42namespace 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
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using log4net;
32using Mono.Addins;
33using Nini.Config;
34using OpenMetaverse;
35using OpenMetaverse.Packets;
36using OpenMetaverse.StructuredData;
37using OpenSim.Framework;
38using OpenSim.Region.Framework;
39using OpenSim.Region.CoreModules.Framework.DynamicAttributes.DAExampleModule;
40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.Scenes;
42
43namespace 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
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using Nini.Config;
32using NUnit.Framework;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Framework.Communications;
36using OpenSim.Framework.Servers;
37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.CoreModules.Framework;
39using OpenSim.Region.CoreModules.Framework.EntityTransfer;
40using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
41using OpenSim.Tests.Common;
42using OpenSim.Tests.Common.Mock;
43
44namespace 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;
39namespace OpenSim.Services.AssetService 39namespace 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
28using System; 28using System;
29using System.Reflection; 29using System.Reflection;
30using log4net;
30using Nini.Config; 31using Nini.Config;
31using OpenSim.Framework; 32using OpenSim.Framework;
32using OpenSim.Data; 33using OpenSim.Data;
34using OpenSim.Server.Base;
33using OpenSim.Services.Interfaces; 35using OpenSim.Services.Interfaces;
34using OpenSim.Services.Base; 36using 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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Net;
32using System.Reflection;
33using System.Threading;
34using log4net;
35using Nini.Config;
36using Mono.Addins;
37using OpenMetaverse;
38using OpenMetaverse.StructuredData;
39using OpenSim.Framework;
40using OpenSim.Framework.Servers;
41using OpenSim.Region.Framework.Interfaces;
42using OpenSim.Region.Framework.Scenes;
43
44namespace 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();