Compare commits

..

608 Commits

Author SHA1 Message Date
Justin Clark-Casey (justincc) 57530c8897 minor: Make note in log if scene was restarted due to an unrecoverable physics error 2012-11-26 23:22:54 +00:00
Justin Clark-Casey (justincc) d3776e8fba Add AllowRegionRestartFromClient setting to [EstateManagement] section of OpenSim.ini.
Setting this to false will block all restart requests from the viewer even if they are otherwise legitimate.
One use is to block region restarts if necessary whilst restart functionality remains buggy or triggers bugs in modules,
though these should be fixed as soon as practicable.
Default is true, as has been the case historically.
2012-11-26 23:22:39 +00:00
Justin Clark-Casey (justincc) a124984177 Remove the redundant BypassPermissions() checks in EstateManagmentModule.
This is repeated in the PermissionsModule and checking it earlier does not allow a force override of the bypass value
2012-11-26 23:20:18 +00:00
Justin Clark-Casey (justincc) 49307ab4d1 minor: Add some console feedback on region restart and log who requested a region restart if done from the viewer. 2012-11-26 23:20:05 +00:00
Justin Clark-Casey (justincc) 2271986396 Lock on AgentCircuitData during Scene.AddClient() and RemoveClient() to prevent an inactive connection being left behind if the user closes the viewer whilst the connection is being established.
This should remove the need to run the console command "kick user --force" when these connections are left around.
2012-11-26 23:19:00 +00:00
Justin Clark-Casey (justincc) e54c11e0da Replace old Prebuild.exe accidentally added back in f977291 with one built without <copy> nant target generation and on mono 2.4.3 2012-11-15 02:51:21 +00:00
Justin Clark-Casey (justincc) 688c91308c Replace previously updated Prebuild.exe with one built directly with mono 2.4.3 2012-11-15 02:51:12 +00:00
Justin Clark-Casey (justincc) cce6e6c841 Stop Prebuild from generating <copy> statements which unnecessarily copy files into bin/Debug or bin/Release
nant_0.91~alpha2+dfsg-3_all.deb in Ubuntu 12.04 and earlier actually ignored these due to a bug
However, nant 0.92~rc1+dfsg-2 in Ubuntu 12.10 fixes this bug (possibly https://github.com/nant/nant/pull/39).
Which makes nant time-consumingly copy these files when the aren't actually used.
Tested removal of <copy> on both nant 0.91 and nant 0.92
Will be submitting this patch to prebuild project for comment though I suspect there's nobody there to pay attention.
2012-11-15 02:51:04 +00:00
Justin Clark-Casey (justincc) 8638766d79 Add [AssetService] AllowRemoteDeleteAllTypes (default false).
This allows a closed grid to delete asset types other than maptile remotely.
Only operational if AllowRemoteDelete = true also.
Defaults to false - do not enable if anybody other than you can make asset service requests.
2012-11-15 02:11:37 +00:00
Justin Clark-Casey (justincc) 7ae128abbe Move check to allow only deletion of maptiles up to AssetServerDeleteHandler from AssetService.
This allows us to use a common check for both AssetService and XAssetService.
It also allows future console commands to delete an asset.
As before, deletion of maptile assets is not allowed remotely unless this is explicitly configured.
2012-11-15 02:11:24 +00:00
Justin Clark-Casey (justincc) 6391a2fad4 minor: Adjust method doc on IXInventoryData MoveItem() and MoveFolder() to make it clear we're moving to an existing folder. 2012-11-13 04:47:24 +00:00
Justin Clark-Casey (justincc) 65d838da00 Update parent inventory folder version numbers when folders are moved/created/deleted to match version numbers cached by viewers.
This is done in the way that one would expect (e.g. moving a folder increments version number on both source and destination parent folders).
This should hopefully improve viewer reuse of its cached inventory information.
Currently MySQL only but will be implement for SQLite/MSSQL if there are no issues.
2012-11-13 04:47:17 +00:00
Justin Clark-Casey (justincc) c8ff599544 Add System.Core reference to OpenSim.Data.MySQL DLL, which is necessary to use LINQ 2012-11-08 22:05:37 +00:00
Justin Clark-Casey (justincc) 10a12b10d5 Update folder version numbers when moving items and making the Delete(string[], string[]) call (not just string, string).
This is to stop viewer inventory cache version numbers becoming out of sync with grid stored numbers when viewer performs these actions.
If there are no problems with these changes, they will be propogated to SQLite (and MSSQL if that's simple enough).
May also need to do the same on folder store/create/delete and maybe propogate version increments up the folder hierarchy, but that requires investigation.
2012-11-08 22:00:44 +00:00
Justin Clark-Casey (justincc) baddfa08f3 Increment version number of a folder when an object it contains is deleted.
Not doing this was allowing the viewer inventory cache to become out of sync if an item was directly deleted.
2012-11-08 22:00:36 +00:00
Justin Clark-Casey (justincc) f6aa262585 Lock GDI+ portion og VectorRenderModule.GetDrawStringSize() to prevent concurrent thread use provoking mono crashes.
Same rationale as commit 13690582.
2012-09-28 01:56:25 +01:00
Justin Clark-Casey (justincc) 2cc7a89012 Fix usage statement on "debug http" console command since max level is now 5 rather than 3 2012-09-13 00:20:38 +01:00
Justin Clark-Casey (justincc) 108e77fe1a Fix bug in logging sample input at debug http level 4.
Also converts newlines to "\n" text.
2012-09-13 00:20:31 +01:00
Justin Clark-Casey (justincc) 97b1b309ce minor: Comment out friends notification log spam for now. 2012-09-13 00:20:21 +01:00
Justin Clark-Casey (justincc) 53839ac779 Make "show http-handlers" command available for ROBUST instances as well as the simulator executable. 2012-09-13 00:20:09 +01:00
Justin Clark-Casey (justincc) 4efcf38e17 Add levels 4 and 5 to "debug http" console command that will log a sample of incoming request data and the entire incoming data respectively.
See "help debug http" for more details.
2012-09-13 00:20:01 +01:00
Justin Clark-Casey (justincc) 639d147231 Add missing DynamicTexture.cs file from last commit 2012-09-13 00:19:45 +01:00
Justin Clark-Casey (justincc) a53f90cf7a If reusing dynamic textures, do not reuse small data length textures that fall below current viewer discard level 2 thresholds.
Viewer LL 3.3.4 and before sometimes fail to properly redisplay dynamic textures that have a small data length compared to pixel size when pulled from cache.
This appears to happen when the data length is smaller than the estimate discard level 2 size the viewer uses when making this GetTexture request.
This commit works around this by always regenerating dynamic textures that fall below this threshold rather than reusing them if ReuseDynamicTextures = true
This can be controlled by the [Textures] ReuseDynamicLowDataTextures config setting which defaults to false.
2012-09-13 00:19:39 +01:00
Justin Clark-Casey (justincc) e0a3a8d47b If the GetTexture capability receives a request for a range of data beyond that of an otherwise valid asset, return HTTP PartialContent rather than RequestedRangeNotSatisfiable.
This is because recent viewers (3.2.1, 3.3.4) and probably earlier ones using the http GetTexture capability will sometimes make such invalid range requests.
This appears to happen if the viewer's estimate of texture sizes at discard levels > 0 (chiefly 2) exceeds the total texture size.
I believe this does not normally happen but can occur for dynamic textures with are large but mainly blank.
If this happens, returning a RequestedRangeNotSatisfiable will cause the viewer to not render the texture at the final resolution.
However, returning a PartialContent (or OK) even with 0 data will allow the viewer to render the final texture.
2012-09-13 00:19:26 +01:00
Justin Clark-Casey (justincc) 7ce30cc49e Make ReuseDynamicTextures an experimental config setting in [Textures]. Default is false, as before.
If true, this setting reuses dynamically generated textures (i.e. created through osSetDynamicTextureData() and similar OSSL functions) where possible rather than always regenerating them.
This results in much quicker updates viewer-side but may bloat the asset cache (though this is fixable).
Also, sometimes issue have been seen where dynamic textures do not transfer to the viewer properly (permanently blurry).
If this happens and that flag is set then they are not regenerated, the viewer has to clear cache or wait for 24 hours before all cached uuids are invalidated.
CUrrently experimental.  Default is false, as before.
2012-09-13 00:19:12 +01:00
Justin Clark-Casey (justincc) 6c36471f0c If the compile-time DynamicTextureModule.ReuseTextures flag is set, check metadata still exists for any reused asset in case some other process has removed it from the cache. 2012-09-13 00:19:05 +01:00
Justin Clark-Casey (justincc) a100723aaf Renaming existing 'torture' tests to 'performance' tests instead, since this better matches what they really do.
nant target name changes to test-perf instead of torture, to match test-stress
still not run by default
2012-09-13 00:18:56 +01:00
Justin Clark-Casey (justincc) 69dc9b89a2 Add VectorRenderModuleStressTests that contains a long running test that generates thousands of vector textures concurrently.
Intended for use if there are future issues with mono crashes whilst generate dynamic textures.
This test is triggered via a new test-stress nant target.
Not run by default.
2012-09-13 00:18:48 +01:00
Justin Clark-Casey (justincc) c0d68d0aa4 Add experimental DynamicTextureModule.ReuseTextures flag, currently only configurable on compile.
Disabled (status quo) by default.
This flag makes the dynamic texture module reuse cache previously dynamically generated textures given the same input commands and extra params for 24 hours.
This occurs as long as those commands would always generate the same texture (e.g. they do not contain commands to fetch data from the web).
This makes texture changing faster as a viewer-cached texture uuid is sent and may reduce simulator load in regions with generation of lots of dynamic textures.
A downside is that this stops expiry of old temporary dynamic textures from the cache,
Another downside is that a jpeg2000 generation that partially failed is currently not regenerated until restart or after 24 hours.
2012-09-13 00:18:29 +01:00
Justin Clark-Casey (justincc) ce89bc38e1 Add VectorRenderModule.TestRepeatSameDrawDifferentExtraParams() 2012-09-13 00:18:07 +01:00
Justin Clark-Casey (justincc) a21ee11fe8 Add VectorRenderModuleTests.TestRepeatDrawContainingImage() 2012-09-13 00:18:00 +01:00
Justin Clark-Casey (justincc) 06df03d98a Add VectorRenderModuleTests.TestRepeatDraw() 2012-09-13 00:17:47 +01:00
Justin Clark-Casey (justincc) 2f4181623d Add IDynamicTextureManager.ConvertData() to match AsyncConvertData(). Remove mismatching ConvertStream() where there is no AsyncConvertStream and neither IDynamicTextureManager implementer implements this method. 2012-09-13 00:13:22 +01:00
Justin Clark-Casey (justincc) bbfb501b02 minor: Simplify return of vector render module name and some very minor removal of unncessary syntax clutter 2012-09-13 00:13:13 +01:00
Justin Clark-Casey (justincc) a70baa95d1 Lock disposal of separate gdi+ objects under different threads since this prevents malloc heap corruption seen under Ubuntu 10.04.1 and 11.04 - probably a libcairo issue
In testing, it appears that if multiple threads dispose of separate GDI+ objects simultaneously,
the native malloc heap can become corrupted, possibly due to a double free().  This may be due to
bugs in the underlying libcairo used by mono's libgdiplus.dll on Linux/OSX.  These problems were
seen with both libcario 1.10.2-6.1ubuntu3 and 1.8.10-2ubuntu1.  They go away if disposal is perfomed
under lock.
2012-08-22 23:09:33 +01:00
Justin Clark-Casey (justincc) ed99017271 Tighten up OpenSim.Framework.Cache locking to avoid race conditions.
This is to resolve a reported issue in http://opensimulator.org/mantis/view.php?id=6232
Here, the land management module is using OpenSim.Framework.Cache (the only code to currently do so apart from the non-default CoreAssetCache).
2012-08-21 22:23:34 +01:00
Justin Clark-Casey (justincc) 061c748b75 Add --force flag to "kick user" console command to allow bypassing of recent race condition checks.
This is to allow a second attempt to remove an avatar even if "show connections" shows them as already inactive (i.e. close has already been attempted once).
You should only attempt --force if a normal kick fails.
This is partly for diagnostics as we have seen some connections occasionally remain on lbsa plaza even if they are registered as inactive.
This is not a permanent solution and may not work anyway - the ultimate solution is to stop this problem from happening in the first place.
2012-08-21 22:23:04 +01:00
Justin Clark-Casey (justincc) c4645a899f Add information to ThreadStackSize about possibly increasing if suffering StackOverflowExceptions during script conversion/compilation (e.g. on Windows 64-bit) 2012-08-21 22:15:17 +01:00
Justin Clark-Casey (justincc) c223ac6929 minor: Make xengine debug message on script load a scripting loading message instead.
This is more useful if compilation fails due to an uncatchable exception since we know what was being compiled.
2012-08-21 22:14:56 +01:00
Melanie 7e287227a4 Release http-in URLs when llResetScript is called 2012-08-21 22:12:48 +01:00
Justin Clark-Casey (justincc) d4470c6147 Fix the recent windows compile error by putting newFont for case "R" in VectorRenderModule inside its own context, rather than disposing of the old font before using it as a prototype for the new. 2012-08-21 22:11:53 +01:00
SignpostMarv c14f983cca attempting to fix a build issue
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-08-21 22:11:39 +01:00
Justin Clark-Casey (justincc) 8a78d0f974 Delete old blank SOGSpamTest 2012-08-21 22:09:00 +01:00
Justin Clark-Casey (justincc) f35e94f168 Properly dispose of all GDI+ entities used in VectorRenderModule for dynamic textures.
The convention is that if an object implements IDiposable() the code must explicitly call Dispose() or call it via the using statement.
This may be particularly important for GDI+ objects since they encapsulate native code entities.
2012-08-21 22:08:49 +01:00
Justin Clark-Casey (justincc) 0e9e60d55a Remove duplicated IScenePresence.PresenceType. This is already in ISceneAgent.PresenceType from which IScenePresence inherits.
No other code changes required.
2012-08-21 22:06:31 +01:00
Justin Clark-Casey (justincc) f6a7325961 Add information about each column to "show queues" region console command help. 2012-08-21 22:00:38 +01:00
Justin Clark-Casey (justincc) ed63cafa65 Extend region console "show queues" command to show already collected time since last packeted received by the simulator from a viewer. 2012-08-21 22:00:31 +01:00
Justin Clark-Casey (justincc) 092e94035b Backport fix of inventory skeleton fetch from XInventoryServicesConnector.
This is not used by login if the inventory service and login service are housed in the same ROBUST instance (which is the default config)
2012-08-02 01:55:44 +01:00
Justin Clark-Casey (justincc) fa3ebd85c9 minor: Comment out noisy ScriptTaskInventory Request log message 2012-08-02 01:36:37 +01:00
Justin Clark-Casey (justincc) 08a9b01123 Fix a bug in pCampbot grabbing behaviour where an exception would be thrown if the bot was not yet aware of any objects. 2012-08-02 01:35:31 +01:00
Justin Clark-Casey (justincc) 5772d23ff6 Look up the NPC module when the SensorRepeat class is created, rather than on every single sensor sweep. 2012-08-02 01:34:53 +01:00
Justin Clark-Casey (justincc) 1d0ff7da2a Resolve a deadlock between INPCModule and SensorRepeat by replacing the SensorRepeat list with a new list on add/removes rather than locking it for the duration of the sensor sweep.
A deadlock was observed today where NPC removal on a script thread would lock the NPC list and then try to lock the sensor list via scripted attachment removal.
Concurrently, the sensor sweep thread would lock the sensor list and then try to lock the NPC list to check NPC status.
This commit resolves the deadlock by replacing the sensor list on update rather than locking it for the duration of the sweep.
2012-08-02 01:30:35 +01:00
Justin Clark-Casey (justincc) ec063b9088 Fix bug in SoundModule.TriggerSound() where every sound update to an avatar would base its gain calculation on the previous avatar's gain, instead of the original input gain.
This was making sound attenuate oddly when there were NPCs in the region, though it could also happen with ordinary avatars.
2012-08-02 01:30:27 +01:00
Justin Clark-Casey (justincc) 12fbfb6125 If we're fetching active gestures via the XInventoryServiceConnector, then properly look at the ITEMS dictionary already returned rather than the level above this. 2012-08-02 01:30:17 +01:00
SignpostMarv 272cd9886d adding asset cache and map tile directories to .gitignore 2012-08-02 01:29:22 +01:00
Justin Clark-Casey (justincc) 627cfe6678 Remove the LandGeom checks in OdeScene - these are pointless since LandGeom is always IntPtr.Zero and contacts returned always have a valid geometry.
Possibly this was for a feature that was never implemented or was otherwise removed.
Thanks to SignpostMarv for the spot of the warning that shows this parameter was never changed.
2012-08-02 01:28:21 +01:00
Justin Clark-Casey (justincc) aac4d9d682 Fix issue where RegionCombinerModule was not removing regions from its dictionary on RemoveRegion(), causing a later issue if regions were restarted (removed then readded). 2012-08-02 01:27:17 +01:00
Justin Clark-Casey (justincc) 25d39a19e1 When copying items, copy the item description field instead of the asset description field.
If we copy the asset description then we will only ever replicate the very first description, if there was one, not any subsequent changes.
Thanks to Oren Hurvitz of Kitely for this patch from http://opensimulator.org/mantis/view.php?id=6107
I have adapted it slightly to change the order of arguments (name before description rather than vice-versa) and slightly improve some method doc.
2012-08-02 01:27:03 +01:00
Justin Clark-Casey (justincc) 55d383e4be Add MemoryWatchdog class missing from git master a1e9964 2012-08-02 01:19:01 +01:00
Justin Clark-Casey (justincc) b180fdd0fd Correct churn stat from MB/s from KB/s 2012-08-02 01:18:48 +01:00
Justin Clark-Casey (justincc) cc6b2fdb9f Add experimental "OpenSim object memory churn" statistics to output of region console "show stats" command
This aims to capture the amount of memory that OpenSim turns over whilst operating a region.
This memory is not lost - apart from leaks it is reclaimed by the garbage collector.
However, the more memory that gets turned over the more work the GC has to do to reclaim it.
2012-08-02 01:13:58 +01:00
Justin Clark-Casey (justincc) 9d6ea27df0 Make SceneManager.OnRegionsReadyStatusChange event available.
This is fired when all regions are ready or when at least one region becomes not ready.
Recently added EventManager.OnRegionReady becomes OnRegionReadyStatusChange to match OnLoginsEnabledStatusChange
2012-08-02 00:50:02 +01:00
Melanie 2d66fcd4cd Committing Avination's memleak fix-a-thon, installment #2
Ensure items coming off the lockless queue are released. Also ensure this
is done when the queue is cleared.
2012-08-02 00:24:23 +01:00
Melanie 91ae1908fe Committing Avination's memleak fix-a-thon, installment #3
When linking, detach the no longer used SOG's from backup so they can be
collected. Since their Children collection is never emptied, they prevent
their former SOPs from being collected as well.
2012-08-02 00:23:47 +01:00
Melanie ed962bb3d3 Commiting Avination's memleak fix-a-thon, installment #1
As the MinHeap shrinks, free object references that have been sent. Also,
free the last item when it empties.
2012-08-02 00:23:38 +01:00
Justin Clark-Casey (justincc) 9ea09d785c Add basic TestCreateRootScenePresence() regression test 2012-08-02 00:18:45 +01:00
Justin Clark-Casey (justincc) e3db5fb603 Add EventManager.OnRegionLoginsStatusChange fired whenever logins are enabled or disabled at any point, not just during initial startup.
This replaces EventManager.OnLoginsEnabled which only fired when logins were first enabled
and was affected by a bug where it would never fire if the region started with logins disabled.
2012-08-02 00:11:11 +01:00
Justin Clark-Casey (justincc) 58aa51218e Add back notification to neighbouring regions when RegionReadyModule is not active accidentally just removed in 528004d 2012-08-02 00:11:00 +01:00
Justin Clark-Casey (justincc) 0fff0e1fad Perform other region ready actions even if simulator is configured to leave logins disabled on startup. 2012-08-02 00:10:46 +01:00
Justin Clark-Casey (justincc) 4457ec667a Stop the 15 second initial script compile wait if a script is being rezzed on a previously empty region. 2012-08-02 00:07:41 +01:00
Justin Clark-Casey (justincc) 7f2e6a55c4 Fix bug where region ready would be triggered a second time if a script was rezzed on a previously script-free region.
There is no need to listen for OnRezScript in RegionReadyModule since OnEmptyScriptCompileQueue will only fire if scripts were compiled.
2012-08-02 00:07:27 +01:00
Justin Clark-Casey (justincc) 92a01a7e70 Fix bug where region ready was being triggered twice in quick succession if a region contained no scripts. 2012-08-02 00:04:37 +01:00
Justin Clark-Casey (justincc) 4debc67b48 Establish EventManager.OnRegionReady event. This will only be triggerred once when the region is ready.
Switch MapImageServiceModule to use this.
2012-08-02 00:04:17 +01:00
Justin Clark-Casey (justincc) c8f60acc30 Pass entire scene object in OnLoginsEnabled event rather than just the region name.
This saves listeners from having to re-retrieve the scene from their own lists, which won't work anyway if multiple regions with the same name have been allowed
2012-08-01 23:59:47 +01:00
Justin Clark-Casey (justincc) 272c3c1069 Only listen to LoginsEnabled event in RegionReadyModule if it has been asked to disable logins until all scripts have been compiled 2012-08-01 23:50:59 +01:00
Justin Clark-Casey (justincc) 5aa9c21e7d minor: switch around mixed up circuit code and endpoint data in "show connections" region console command 2012-07-25 23:37:21 +01:00
Justin Clark-Casey (justincc) 454c3fc913 Make LLClientView instant message handling asynchronous rather than synchronous to prevent long operations from holding up all inbound packet processing.
Giving a large folder from one avatar to another was causing a long delay when handled synchronously, since it took some time to retrieve the necessary data from the inventory service.
Handling this asynchronously instead stops this delay from disrupting all avatars in the scene.  This has been shown in OSGrid.
I see no reason for not handling all IM messages asynchronously, just as incoming chat is handled asynchronously, so this has been switched for all instant messages.
Thanks to Nebadon for testing this change out.
2012-07-25 23:37:20 +01:00
Justin Clark-Casey (justincc) 3cd7c59696 Comment out OnIncomingInstantMessage and OnInstantMessage handlers in GroupsModule, since these led to a private blank method 2012-07-25 23:37:18 +01:00
Justin Clark-Casey (justincc) 1f8ac33ecb Stop explicitly closing and nulling out Animator in order to prevent NREs in various places due to race conditions.
Even where checks are being made they aren't enough since they all assume that the Animator they just checked is still there in the next line, which is not necessarily the case without locking.
The memory used is small and these should be GC'd anyway when the SP is released.  If this is not happening then the wider problem of old SPs being retained needs to be resolved.
2012-07-25 23:37:15 +01:00
Justin Clark-Casey (justincc) 47e2922a40 Prevent race conditions between two threads that call LLClientView.Close() simultaneously (e.g. ack timeout and an attempt to reconnect) 2012-07-25 23:32:27 +01:00
Justin Clark-Casey (justincc) 4deb25da87 Add TestCreateDuplicateRootScenePresence() regression test. 2012-07-25 23:32:21 +01:00
Justin Clark-Casey (justincc) 587c8017ab Add basic TestCreateRootScenePresence() regression test 2012-07-25 23:32:06 +01:00
Justin Clark-Casey (justincc) d5d801f218 Close() the ScenePresence after we've removed it from the scene graph, to cut down race conditions when another thread manages the grab the presence after some SP structures have been reset. 2012-07-25 23:30:59 +01:00
Justin Clark-Casey (justincc) 2f865da5c7 refactor: factor out common code in WebStatsModule.OnMakeRootAgent() 2012-07-19 01:00:50 +01:00
Justin Clark-Casey (justincc) cf2f6843c6 In WebStatsModule.OnMakeRootAgent(), get region ID directly from SP.Scene.RegionInfo.RegionID instead of manually looking it up from the stored scene list. 2012-07-19 01:00:40 +01:00
Justin Clark-Casey (justincc) 97d084c9f5 Stop warning about no session from ViewerStats if user teleports to another region in the same simulator that was not next to the source region.
This was because teleporting to the new region invoked the new session setup code before the agent was removed from the old region, which then invoked the session teardown code.
Now, we only invoke the teardown code if the region ID occupied by the agent being removed is the same as the one registered for the current session.
2012-07-19 01:00:33 +01:00
Justin Clark-Casey (justincc) 14f72a9a43 Remove a callstack print out I accidentally left in 2 commits ago in 9ccb578 2012-07-19 00:57:50 +01:00
Justin Clark-Casey (justincc) 89efccaa71 Rather than instantiating a UTF8 encoding everywhere when we want to supress the BOM, use a single Util.UTF8NoBomEncoding.
This class is thread-safe (as evidenced by the provision of the system-wide Encoding.UTF8 which does not suppress BOM on output).
2012-07-19 00:57:36 +01:00
Justin Clark-Casey (justincc) 8f61da0759 Don't cache regions data on the other unused LocalGridServiceConnector that the module code still sets up even if we're using one directly instantiated from the RemoteGridServiceConnector.
Also improves log messages to indicate which regions are sending/receiving various neighbour protocol messages.
2012-07-19 00:50:17 +01:00
Justin Clark-Casey (justincc) e88e87ff63 Stop redundantly passing in the endpoint to the LLClientView constructor.
This can always be retrieved via the LLUDPClient and is so done in various places already.
2012-07-19 00:45:19 +01:00
Justin Clark-Casey (justincc) 4f10d1aa0c Remove IClientIPEndpoint client interface for now.
This may well come back in the future when this subinterface is actually used but it currently isn't and I feel the name was poor.
Everything uses IClientAPI.RemoveEndPoint which also returned the full endpoint rather than just the ip address.
2012-07-19 00:45:08 +01:00
Justin Clark-Casey (justincc) 1912215c40 Remove IClientAPI.GetClientEP() in favour of existing identical IClientAPI.RemoteEndpoint. 2012-07-19 00:45:02 +01:00
Justin Clark-Casey (justincc) ffbca99b57 Extend "show circuits" to show circuit code, ip and viewer name.
Also change to use standard table formatting
"show circuits" and "show connections" console commands are very similar but access different data structures.
2012-07-19 00:44:48 +01:00
Justin Clark-Casey (justincc) f660a25fa7 Add active status to "show connections" 2012-07-19 00:40:35 +01:00
Justin Clark-Casey (justincc) 523d03fb76 Put output for "show connections" command into standard table format.
Also moves into own method.
2012-07-19 00:40:18 +01:00
Justin Clark-Casey (justincc) 85985a8c3e Where possible, use the system Encoding.ASCII and Encoding.UTF8 rather than constructing fresh copies.
The encodings are thread-safe and already used in such a manner in other places.
This isn't done where Byte Order Mark output is suppressed, since Encoding.UTF8 is constructed to output the BOM.
2012-07-19 00:25:40 +01:00
Justin Clark-Casey (justincc) 013e002b00 Fix build break for windows with missing package for IScriptEngine in prebuild.xml for OpenSim.Region.CoreModules.Tests.dll 2012-07-11 23:29:45 +01:00
Justin Clark-Casey (justincc) 5269d77cf9 Allow XEngine StartDelay to be configured in the [XEngine] config section.
This is only currently meant for use by regression tests that don't have any issues if XEngine is started up quickly, since no other operations will be occuring simultaneously.
Therefore, this is not yet documented externally.
2012-07-11 23:29:39 +01:00
Justin Clark-Casey (justincc) 8c8e6220fb Remove WorldComm module from the regression TestCompileAndStartScript() since the infrastructure no longer fails if this module isn't present, at least on the tested codepaths 2012-07-11 23:29:31 +01:00
Justin Clark-Casey (justincc) 150860c964 Add regression TestDetachScriptedAttachmentToInventory()
This currently only does a relatively crude check for a ScriptState node in the serialized xml
2012-07-11 23:29:24 +01:00
Justin Clark-Casey (justincc) 0cb1b0bb4a Add regression TestRezScriptedAttachmentsFromInventory() though this currently only checks for the presence of script items, not for started scripts 2012-07-11 23:29:16 +01:00
Justin Clark-Casey (justincc) 56d894ae24 refactor: move management of SOP.SitTargetAvatar into SOP.AddSittingAvatar() and SOP.RemoveSittingAvatar() 2012-07-11 23:29:07 +01:00
Justin Clark-Casey (justincc) b34fd50155 Revert "refactor: Add SOP.IsSitTargetOccupied to improve readability"
This reverts commit c8f0d476d2.
On reconsideration, I think this is less readable since immediately following code still sets SitTargetAvatar directly
2012-07-11 23:15:35 +01:00
Justin Clark-Casey (justincc) 74486e767d refactor: Add SOP.IsSitTargetOccupied to improve readability 2012-07-11 23:15:23 +01:00
Justin Clark-Casey (justincc) 1b8814878d refactor: use sit orientation argument passed in to SP.SendSitResponse() rather than creating a new copy
There are no issues with side-effects since this is a struct.
2012-07-11 23:15:14 +01:00
Justin Clark-Casey (justincc) 095375d63d Move common code to detect whether a part has a valid sit target into a SOP property rather than being repeated in SP.
This also makes the detection in SP.FindNextAvailableSitTarget() and SendSitResponse() identical.
Previously they varied slightly (SendSitResponse didn't check for an older type of invalid quaternion) but the practical effect is most probably zero.
2012-07-11 23:15:06 +01:00
Justin Clark-Casey (justincc) cb4e074a8c Remove log line accidentally left in SP.SendSitResponse() 2012-07-11 23:14:59 +01:00
Justin Clark-Casey (justincc) 222f844d65 Disable logging in regression test in OSSL_ApiAttachmentTests 2012-07-11 23:14:52 +01:00
Justin Clark-Casey (justincc) bb2a9060f1 Fix recent SOP.GetSittingAvatars() to return null if there are no sitting avatars rather than throwing an exception.
Extends sitting avatar regression tests to test new sitters information
2012-07-11 23:14:45 +01:00
Justin Clark-Casey (justincc) e21dd88ed5 When an attachment is detached to inv or derezzed, stop the scripts, update the known item with script state still in the script engine and then remove the scripts.
This is to fix a regression starting from 5301648 where attachments had to start being deleted before persistence in order to avoid race conditions with hud update threads.
2012-07-11 23:14:36 +01:00
Justin Clark-Casey (justincc) 35457448ca If a script is being stopped manually, then give the scriptpool thread 1 second to finish normally before forcibly aborting.
This is to avoid the worst of the problems in mono 2.6, 2.10 where an aborted thread does not always release all its locks.
This very short grace period is identical to the existing behaviour when a script is removed from the scene.
2012-07-11 23:14:30 +01:00
Justin Clark-Casey (justincc) 596caf41db This script allows an object to be attached directly from prim inventory to another avatar in the scene.
Very useful in serious game/environment scenarios where its only allowed for trusted creators.
Threat level Severe
2012-07-11 23:14:14 +01:00
Justin Clark-Casey (justincc) c62b46a167 Don't allow a prim to be sat upon if its part of an attachment 2012-07-11 23:14:04 +01:00
Justin Clark-Casey (justincc) 2b3098f011 Do not allow a script to attach a prim if its being sat upon.
This prevents a stack overflow where a get position on the avatar will refer to the attachment which will in turn refer back to the avatar.
This required recording of all sitting avatars on a prim which is done separately from recording the sit target avatar.
Recording HashSet is null if there are no sitting avatars in order to save memory.
2012-07-11 23:13:48 +01:00
Justin Clark-Casey (justincc) 74b4efd7f9 minor: remove some recent mono compiler warnings 2012-07-11 23:07:17 +01:00
Justin Clark-Casey (justincc) 298c4c0eb5 Remove duplicate Warp3DImageModule entry in CoreModulePlugin.addin.xml
This was causing 2 copies of the module to be created for each scene.
Probably no bad consequences other than a small waste of memory (both for the module and for the warp3D renderer it loaded)
2012-07-11 23:07:09 +01:00
Justin Clark-Casey (justincc) 068917bc29 Remove redundant SetScene() function in Scene.AddSceneObject()
This is always done later on in SceneGraph.AddSceneObject() if the call hasn't failed due to sanity checks.
There's no other purpose for this method to exist and it's dangerous/pointless to call in other conditions.
2012-07-11 23:06:41 +01:00
Justin Clark-Casey (justincc) d8f1d2892c Remove now duplicate interregion object check that should have been removed a few commits ago in 43a2da9 2012-07-11 23:06:30 +01:00
Justin Clark-Casey (justincc) ccf8e89193 minor: Remove some wrong comments in attachments regression tests 2012-07-11 23:06:23 +01:00
Justin Clark-Casey (justincc) f17f58ac2a Add assert to attachment regression tests to check that number of objects in the scene graph 2012-07-11 23:06:14 +01:00
Justin Clark-Casey (justincc) 6bcb5baab6 Pull prim crossing/teleport checks up into Scene.IncomingCreateObject() from Scene.AddObject()
Only IncomingCreateObject() needs these checks.  General object adding does not need to perform crossing perm checks
2012-07-11 23:05:59 +01:00
Justin Clark-Casey (justincc) 0aaf935333 refactor: extract method UpdateUserInventoryWithAttachment() from AttachObject() for better code comprehension 2012-07-11 23:05:53 +01:00
Justin Clark-Casey (justincc) b30806822e Log MONO_THREADS_PER_CPU value on simulator startup, or "unset" if it is not set 2012-07-11 23:05:43 +01:00
Justin Clark-Casey (justincc) a2c3dfc422 Log warning if time between invocations of the watchdog thread is twice the timer setting.
This is to help detect situations where thread timeout warnings are being generated because of general machine issues rather than deadlock, network or other problems.
2012-07-11 23:05:34 +01:00
Justin Clark-Casey (justincc) 4050a6f8bb minor: add client name to various login service log messages to disambiguate messages from concurrent logins. Also adds destination resolution debug log message showing region endpoint.
Adding endpoint to the log helps to find issues where the region external host information has been wrongly configured
2012-07-11 23:05:26 +01:00
Justin Clark-Casey (justincc) 0d0d04c484 minor: Add more information to OSFunctionThreatLevel and clarify some text in using PARCEL_OWNER, PARCEL_GROUP_MEMBER, ESTATE_MANAGER, ESTATE_OWNER permission categories 2012-07-11 23:05:09 +01:00
SignpostMarv 952b3448a6 porting console commands from raw2sculpt 3.2 2012-07-11 23:05:01 +01:00
Justin Clark-Casey (justincc) d53ef3b302 Add OSSL function osForceAttachToAvatarFromInventory()
This works like osForceAttachToAvatar() but allows an object to be directly specified from the script object's inventory rather than forcing it to be rezzed in the scene first.
Still only attaches objects to the owner of the script.
This allows one to bypass the complicated co-ordination of first rezzing objects in the scene before attaching them.
Threat level high.
2012-07-11 23:04:49 +01:00
Justin Clark-Casey (justincc) 48e88397a0 Use GetInventoryItem() in llRezAtRoot rather than iterating through a cloned dictionary 2012-07-11 23:04:42 +01:00
Justin Clark-Casey (justincc) 976190fe96 refactor: In llGetNotecardLine() use existing GetInventoryItem() rather than inspecting a clone of the TaskInventory dictionary that was not cloned thread-safe 2012-07-11 23:04:34 +01:00
Justin Clark-Casey (justincc) 1e025d6074 refactor: In llGetNumberOfNotecardLines() use existing GetInventoryItem() rather than inspecting a clone of the TaskInventory dictionary that was not cloned thread-safe 2012-07-11 23:04:27 +01:00
Justin Clark-Casey (justincc) fffd2fe10d refactor: use existing GetInventoryItem() in GetScriptByName(), itself renamed from ScriptByName() 2012-07-11 23:01:41 +01:00
Justin Clark-Casey (justincc) cf8adbe007 refactor: In llGetInventoryType() use existing GetInventoryItem() 2012-07-11 23:01:33 +01:00
Justin Clark-Casey (justincc) 890f617e58 refactor: In llGetInventoryCreator() use existing GetInventoryItem() 2012-07-11 23:01:27 +01:00
Justin Clark-Casey (justincc) 6ff0e58db7 refactor: In llGetInventoryPermMask() use existing GetInventoryItem() 2012-07-11 23:00:57 +01:00
Justin Clark-Casey (justincc) 795443cff1 refactor: in llGetInventoryPermMask use existing GetInventoryItem() 2012-07-11 23:00:50 +01:00
Justin Clark-Casey (justincc) 8c7f511e55 refactor: In llRemoteLoadScriptPin() use existing GetInventoryItem() 2012-07-11 23:00:43 +01:00
Justin Clark-Casey (justincc) 0acac2f890 refactor: replace use of LSL_Api.GetTaskInventoryItem() with existing GetInventoryItem() 2012-07-11 23:00:36 +01:00
Justin Clark-Casey (justincc) 72554fc5b8 refactor: In llGetInventoryKey() use existing GetInventoryItem() 2012-07-11 23:00:28 +01:00
Justin Clark-Casey (justincc) aa1442b07e In llMessageLinked() use GetInventoryItems() rather than cloning TaskInventory directory
GetInventoryItems() returns a new list and so is equivalent, and creates this list under lock whereas Clone() is not thread-safe
2012-07-11 23:00:21 +01:00
Justin Clark-Casey (justincc) e8a0ec1287 In llRequestInventoryData() use GetInventoryItems() rather than cloning TaskInventory directory
GetInventoryItems() returns a new list and so is equivalent, and creates this list under lock whereas Clone() is not thread-safe
2012-07-11 23:00:13 +01:00
Justin Clark-Casey (justincc) 3436be2046 refactor: In llCollisionSound() use existing GetInventoryItem() method rather than have it iterate through TaskInventory itself. 2012-07-11 23:00:07 +01:00
Justin Clark-Casey (justincc) 9a0bcb7750 refactor: In llRemoveInventory() use existing GetInventoryItem() method rather than have it iterate through TaskInventory itself. 2012-07-11 22:59:59 +01:00
Justin Clark-Casey (justincc) 218fe36f84 refactor: make llGiveInventory() use existing GetInventoryItem() method rather than iterate through TaskInventory itself. 2012-07-11 22:59:53 +01:00
Justin Clark-Casey (justincc) 07298c8b4f refactor: rename Watchdog.WATCHDOG_TIMEOUT_MS to DEFAULT_WATCHDOG_TIMEOUT_MS to reflect what it actually is 2012-07-11 22:59:46 +01:00
Justin Clark-Casey (justincc) d9f40d1ebc minor: Add some method doc to HasGroupChanged and Schedule GroupForFull/PartUpdate() to indicate when region modules need to invoke them 2012-07-11 22:59:39 +01:00
Justin Clark-Casey (justincc) eddff428ed Removing unused handling of incoming create object by userID and itemID only.
It appears this was never actually used since attachments were rezzed in other code.
This was never available on remote simulator comms, only local.
2012-07-11 22:58:57 +01:00
Justin Clark-Casey (justincc) 61aaa10460 Remove code listed for removal in 0.7.3 that handled script restart for incoming attachments from pre-fatpack regions (versions of OpenSimulator more than a year old) 2012-07-11 22:58:50 +01:00
Justin Clark-Casey (justincc) e1d6929e61 Move update of the final optional ODE total frame stat inside the OdeLock rather than outside to avoid a very occasional race condition with the stat collection thread 2012-07-11 22:58:37 +01:00
Justin Clark-Casey (justincc) 6b87873aa7 refactor: rename _collisionEventPrim to m_collisionEventActors and _collisionEventPrimChanges to m_collisionEventActorsChanges to reflect their actual contents.
These dictionaries handle all actor types, not just physical prims.
2012-07-11 22:58:29 +01:00
Justin Clark-Casey (justincc) f690acbfb6 Fix a regression where we stopped removing avatars from collision event reporting on logout, rather than stopping clearing their collision events.
This occurred in b18c8c8 (Thu May 17 2012).
This was a cause of very occasional race conditions and likely memory leakage as clients came and went from the region.
2012-07-11 22:58:09 +01:00
Justin Clark-Casey (justincc) f49c850269 If a link points to a non-existing item in FetchInventory caps, then don't try to add it to the return data rather than suffering an exception later on 2012-07-11 22:58:01 +01:00
Justin Clark-Casey (justincc) 70f85af75b Add IScene.Name for code clarity to replace the RegionInfo.RegionName used in many, many log messages. 2012-07-11 22:57:53 +01:00
Justin Clark-Casey (justincc) 663b0cc681 Change AttachmentsModule.DetachSingleAttachmentToInv() to accept a SOG directly instead of an item ID to then shuffle through attachments, saving CPU busywork.
Almost all callers already had the sog to hand.
Still checking that it's really an attachment, but now by inspecting SOG.AttachedAvatar
2012-07-11 22:57:39 +01:00
Justin Clark-Casey (justincc) 733a8c9f89 Rather than iterating through all SOGs in the scene looking for the one that matches out fromItemID on detach, go through the agent's attachment sog list instead. 2012-07-11 22:57:31 +01:00
Justin Clark-Casey (justincc) 25109c8e4d Remove code that tried to delete an attachment back to inventory if RezSingleAttachmentFromInventoryInternal() returned null.
null would only ever be returned if the item couldn't be located within inventory and this would happen immediately.
In this case, derezzing wouldn't work anyway since there is no item to derez.
2012-07-11 22:57:22 +01:00
Justin Clark-Casey (justincc) 51724128bf If rest of first line after colon is blank then still warn about running in XEngine if engine specified does not exist.
This is to take account of situations where the user was intending to specify a script engine using colon using its default language.
This probably generates few false positive as scripts are less likely to end a first line colon with a comment for other purposes.
2012-07-11 22:57:12 +01:00
Justin Clark-Casey (justincc) 461831a65e Avoid reporting false positives when a colon is in a comment in the first line of a script where the user was not trying to select a different script engine.
This works by only posting the "Selected engine unavailable" message if we're falling back on XEngine and the language is one handled by XEngine.
In cases where the language is not handled or not allowed, the user will still be notified by the later compiler error.
This avoids the overwhelming majority of false positives where the first line contains a : for other reasons (e.g. source control systems, vim settings, etc.)
Ultimately, I think it would be better to detect script language/engine with a mechanism that didn't just rely on : detection (e.g like #! in unix scripts).
2012-07-11 22:56:59 +01:00
Justin Clark-Casey (justincc) c6fa09c3af minor: reuse colon index calculation in XEngine.OnRezScript. The index if a colon is found on the first line will always be the same as for the whole script. 2012-07-11 22:56:45 +01:00
Justin Clark-Casey (justincc) 9ccf56eaae Fix issue in InventoryArchiveTestCase where it didn't call down to OpenSimTestCase.SetUp() 2012-06-27 21:52:23 +01:00
Justin Clark-Casey (justincc) ea9b0794e0 refactor: Move ScenePresence <-> AgentData attachments copying code into AttachmentsModule. 2012-06-27 21:52:16 +01:00
Justin Clark-Casey (justincc) 96191241be Automatically disable log4net before each regression test so that logging is confined to a single test if it's turned on.
This involves making test classes inherit from a common OpenSimTestCase.
This will be applied to more classes as required.
2012-06-27 21:52:08 +01:00
Justin Clark-Casey (justincc) 779f0ede49 Fix output for help on some object region console commands 2012-06-27 21:51:54 +01:00
Justin Clark-Casey (justincc) e511106d8d Show region positions in "show regions" robust console command 2012-06-27 21:51:48 +01:00
Justin Clark-Casey (justincc) 37f22e2946 minor: correct GridService "show regions" cibsike cinnabd usage statement 2012-06-27 21:51:40 +01:00
Justin Clark-Casey (justincc) b802906827 refactor: Remove unnecessary AttachmentModuleTests.m_userId in favour of local variables 2012-06-27 21:51:34 +01:00
Justin Clark-Casey (justincc) c6fb5f0fe4 refactor: Use local attachment module variables instead of global m_attMod.
This also avoids confusion between tests where one sets up m_attMod and another accidentally uses it after failing to set one up itself.
2012-06-27 21:51:27 +01:00
Justin Clark-Casey (justincc) 8909e70fc3 refactor: make m_presence a local variable in all AttachmentsModuleTests since it doesn't need to be global and some tests set up more than one sp 2012-06-27 21:51:19 +01:00
Justin Clark-Casey (justincc) 78fcee1f4b Add regression test to check that attachments in source region are deleting when an agent teleports to a neighbouring region 2012-06-27 21:51:12 +01:00
Justin Clark-Casey (justincc) 3106a0f25e If crossing attachments into another region pre-fatpack, clone objects before changing properties to avoid hud display race condition with update threads.
This matches behaviour in fatpack crossing, where attachments are cloned before their properties are changed.
This only applies to crossings to simulators running code released before April 2011.
2012-06-27 21:51:03 +01:00
Justin Clark-Casey (justincc) 19c2f08a8b Add much easier ConsoleDisplayTable AddColumn() and AddRow() methods.
Use these for new "show regions" command rather than old cumbersome stuff.
2012-06-27 21:50:52 +01:00
Justin Clark-Casey (justincc) 005ca12a99 Add "show regions" console command to ROBUST to show all regions currently registered.
Command is not added in standalone, which has its own version of "show regions" that can also show estate name
2012-06-27 21:50:44 +01:00
Justin Clark-Casey (justincc) 02fe1a676e Add "show region at" command to grid service to get the details of a region at a specific location. "show region" command becomes "show region name" to disambiguate
This is the same format as used by "show object name", etc.
"deregister region" also becomes "deregister region id"
2012-06-27 21:50:38 +01:00
Justin Clark-Casey (justincc) 03fc8cf155 minor: update currently commented out log message at top of AvatarFactoryModule.SetAppearance() for future use 2012-06-27 21:50:30 +01:00
Justin Clark-Casey (justincc) 8dedd91961 When attachments are being saved and deleted for a closing root agent, delete first to avoid a hud race condition with update threads.
If delete doesn't occur first then the update thread can outrace the IsAttachment = false necessary to save attachments and send hud artifacts to other viewers.
2012-06-27 21:50:22 +01:00
Justin Clark-Casey (justincc) 0ac040d9ca In AttachmentsModule.DetachSingleAttachmentToInvInternal(), remove attachment before changing properties for correct inventory serialization.
Serialization of attachments requires IsAttachment = false so that correct positions are serialized instead of avatar position.
However, doing this when a hud is still attached allows race conditions with update threads, resulting in hud artifacts on other viewers.
This change sets SOG.IsDeleted before serialization changes take place (IsDeleted itself is not a serialized property).
LLClientView then screens out any deleted SOGs before sending updates to viewers.
2012-06-27 21:50:09 +01:00
Justin Clark-Casey (justincc) c5e5308120 Resolve various race conditions between accessing and removing external script URLs by more consistently locking on m_UrlMap 2012-06-27 21:45:07 +01:00
Justin Clark-Casey (justincc) 1cfaacb88b Avoid a race condition where an incoming request to a script external URL can trigger an exception is the URL was being removed at the same time.
This involves three steps
1) Return gracefully in UrlModule.HttpRequestHandler() instead of throwing an exception when the url cannot be found in its index
2) Return true instead of false in HasEvents() if no matching request is found in the map.  This call will only happen in the first place for raced requests.
3) Return a 404 in GetEvents() if the request is not in the index, rather than a blank 200 OK.

Many thanks to Tom Haines in http://opensimulator.org/mantis/view.php?id=6051 for doing some of the work on this.
2012-06-27 21:45:00 +01:00
Justin Clark-Casey (justincc) 689cafec63 Avoid race condition between m_PrimObjects iteration in XEngine.PostObjectEvent and places where the list is modified by extending the m_PrimObjects lock. 2012-06-27 21:44:53 +01:00
Justin Clark-Casey (justincc) d4cd9e050b If starting scripts on initial sim start, provide INFO level log feedback each time 50 scripts have been started.
This is to provide an indication of what's happening now that the default isn't to report every single script start.
Changes XEngine logging level in OpenSim.exe.config from WARN to INFO.
2012-06-27 21:44:45 +01:00
Justin Clark-Casey (justincc) 472785a5e8 Eliminate unnecessary extra call to TriggerEmptyScriptCompileQueue in XEngine.DoOnRezScriptQueue()
The later invocation of this function will happen on an empty compile queue.
2012-06-27 21:44:38 +01:00
Justin Clark-Casey (justincc) da28fcd357 Stop flicking IsAttachment false and then true in AttachmentsModule.UpdateAttachmentPosition() in order to avoid a hud update race condition.
Previously, setting IsAttachment to false then true was necessary to serialize the updated attachment object information.
However, UpdateAttachmentPosition no longer does this update.
Whilst IsAttachment is set to false there is a race condition where the update thread can wrongly send hud object updates to clients that do not own the hud, resulting in screen artifacts.
2012-06-27 21:44:20 +01:00
Justin Clark-Casey (justincc) 1999338773 Fix bug where attachments would not retain position if just rotated and not moved.
This was because we were not setting AttachedPos in SOG.UpdateGroupPositionPR, unlike UpdateGroupPosition
2012-06-27 21:44:12 +01:00
Justin Clark-Casey (justincc) 95670d2086 refactor AttachmentsModule tests to use a common method for standard attachment item setup 2012-06-27 21:44:04 +01:00
Justin Clark-Casey (justincc) af2c48449c Add regression test for updating attachment position 2012-06-27 21:43:58 +01:00
Justin Clark-Casey (justincc) ffc6110edf Add item id, name, prim name and id to log message when state exists but loading fails.
Drop logging about memory limit exceeded to warn from error
2012-06-27 21:43:49 +01:00
Justin Clark-Casey (justincc) 1edd1f93c1 Retrigger build - last jenkins run was glitched 2012-06-27 21:41:49 +01:00
Justin Clark-Casey (justincc) b6e42da21a Add state file location to errors logged when there's some issue with retrieving state (e.g. exceeds memory limit) 2012-06-27 21:41:34 +01:00
Justin Clark-Casey (justincc) 2e0402433d Use HasPrivateAttachmentPoint properties in SOG.DeleteGroupFromScene() instead of magic numbers 2012-06-27 21:41:24 +01:00
Justin Clark-Casey (justincc) d19600e257 refactor: use SOG.HasPrivateAttachmentPoint in SOP.SendTerseUpdateToClient() instead of attachmentpoint magic numbers. 2012-06-27 21:41:15 +01:00
Justin Clark-Casey (justincc) a6d97e6353 As with LLSDInventoryItem from commit 01a2b0b, send type values in LLSDInventoryFolder for inventory CAPs as integers rather than strings.
Should also resolve some issues with exceptions being thrown in some inventory fetches.
2012-06-20 23:56:25 +01:00
Justin Clark-Casey (justincc) 8eb39eb3ac Lower warn logging on not having friends/group module on permissions to debug.
It's a valid configuration not to have these modules, but I think it's still worth logging the fact that certain permissions won't work (always return true)
2012-06-20 23:55:58 +01:00
Justin Clark-Casey (justincc) 553ac6335d minor: Lower flotsam asset cache warning about not having a FlotsamCache.ini to debug
It's perfectly okay not to have this section.
2012-06-20 23:55:51 +01:00
Justin Clark-Casey (justincc) 32a2515817 Change default logging level for XEngine to WARN instead of DEBUG.
This is to reduce log spam from script loading, which is especially spammy for avatar movements with scripted attachments.
All important messages are at warn or above.
If you still want/need to see these messages, set <level value="DEBUG"/> in the <logger name="OpenSim.Region.ScriptEngine.XEngine"> section of OpenSim.exe.config.
This affects no other package logs, which still output at the root configured level (currently DEBUG by default).
2012-06-20 23:55:42 +01:00
Justin Clark-Casey (justincc) a0482bccc7 Remove STARTUP COMPLETE message from the startuplogo.txt file and into main logging 2012-06-20 23:55:36 +01:00
Justin Clark-Casey (justincc) 3291e256ba Comment out the neighbour and land in connectors from info logging that they are starting up 2012-06-20 23:55:27 +01:00
Justin Clark-Casey (justincc) fb7573f713 Comment out recently added log message detailing number of scripts started when compile queue empties for now 2012-06-20 23:55:16 +01:00
Justin Clark-Casey (justincc) 4684207d6e Raise some IO associated Exception logging in XEngine to error level, in line with other similar cases.
Remove more unnecessary Close() calls - these are being triggered by the Dispose() called when exiting the using statement for these sdk io objects.
2012-06-20 23:55:09 +01:00
Justin Clark-Casey (justincc) 499b778391 Log how many scripts are candidates for starting and how many are actually started.
Adds DebugLevel infrastructure to XEngine though currently commented out and unused.
2012-06-20 23:54:16 +01:00
Justin Clark-Casey (justincc) 9779ceded5 If RegionReady is active, don't falsely say that logins are enabled in the main scene loop before RegionReady is signalled when initial script compilation finishes.
Also raises this logging level to Info from Debug since this information is of high importance.  This matches the behaviour of the RegionReady module
2012-06-20 23:44:28 +01:00
Justin Clark-Casey (justincc) 512d0ac411 minor: If logging because mesh/sculpt data isn't present for an object, log object UUID rather than local id, since UUID doesn't potentially vary between simulator starts. 2012-06-20 23:44:16 +01:00
Justin Clark-Casey (justincc) 981c7d63a0 Like the assembly and text files, only write the c#-lsl linemap in XEngine.SetXMLState() if the trust binaries flag is set.
This doesn't affect other locations where the map is written, such as on script compilation.
2012-06-20 23:44:03 +01:00
Justin Clark-Casey (justincc) 7bd1601a3f minor: Add a little more detail to IOException logging in XEngine.SetXMLState()
Also removes superflous Close() commands for statements taking place within using() constructs
Also adds some comment out debug log messages for future use.
2012-06-20 23:43:47 +01:00
Justin Clark-Casey (justincc) 3aef006e78 Change read config paramter from max_urls_per_simulator to max_external_urls_per_simulator, which is what it was meant to be 2012-06-20 23:33:54 +01:00
Justin Clark-Casey (justincc) 9a6aa528db Fix bug introduced in commit c6e3752 (13 Jun 2012) where poll responses would always return OK even if some other status code had been set 2012-06-20 23:33:46 +01:00
Justin Clark-Casey (justincc) 02a163848c Implement max_external_urls_per_simulator setting in [LL-Functions] to allow configuration of how many urls can be set up by llRequestURL()
Defaults remains as 100.
This setting is per simulator instead of per region due to how the url script module is structured.
2012-06-20 23:33:38 +01:00
Justin Clark-Casey (justincc) b9f122be07 Add region name to UseCircuitCode log messages 2012-06-20 23:33:27 +01:00
Justin Clark-Casey (justincc) e64ca361df Put all debug console commands into a single Debug section rather than scattering them over other categories 2012-06-20 23:33:13 +01:00
Justin Clark-Casey (justincc) 8a11c4e7d4 Make the "debug http" command available for robust as well as the simulator. This allows one to see incoming requests as they happen.
This required making everything use the common MainServer class for registering and retrieving http servers, rather than duplicate structures.
2012-06-20 23:33:04 +01:00
Justin Clark-Casey (justincc) 584a076bec Add main instance to internal MainServer.m_Servers list to simplify internal logic.
This does require the server to be added before it is set as the main Instance
2012-06-20 23:32:57 +01:00
Justin Clark-Casey (justincc) 88596d6097 minor: Tell user the current debug http level if "debug http" console command is executed without a level parameter 2012-06-20 23:32:44 +01:00
Justin Clark-Casey (justincc) 84d97b3bc0 When setting debug http level, do this for all known http servers, not just the main instance. 2012-06-20 23:32:38 +01:00
Justin Clark-Casey (justincc) 2b4e97eeaf Make XMLRPCModule use an existing HTTP server if one already exists on the desired port. 2012-06-20 23:32:30 +01:00
Justin Clark-Casey (justincc) a544280ef2 Get rid of some unnecessary null checks in RegionApplicationBase.StartupSpecific() - a constructor can never return null.
Also adds some method doc to MainServer
2012-06-20 23:32:23 +01:00
Justin Clark-Casey (justincc) 3a0f9836f3 minor: Extend 'debug http' usage statement to 0..3 from 0..2 2012-06-20 23:32:12 +01:00
Justin Clark-Casey (justincc) 4bfac5688d Shuffle "debug http" levels so that 1 and 2 now cause different levels of warn to be logged if we receive invalid xml for xmlrpc. 2012-06-20 23:32:06 +01:00
Justin Clark-Casey (justincc) 0fb93042c6 Fix a regression in BaseHttpServer.HandleXmlRpcRequests() from recent c6e3752
Accidentally make responseString null by default instead of String.Empty.
It needs to be something in case the XmlRpcRequest deserialize throws an exception due to bad xml (a failure which we silently swallow!)
2012-06-20 23:31:50 +01:00
Melanie a3586a7c4b Fix not sending TransferInfo when an asset is not found. This clogs
up the sound pipeline in the viewer.
2012-06-20 23:31:02 +01:00
Justin Clark-Casey (justincc) d4ff56710b Don't include time to transmit response back to requester when assessing slow handling of requests.
This is to avoid logging a 'slow' request when the source of delay is the viewer in processing a response.
This is not something we can do much about on the server end - it's server-side delay that we're interested in.
To ensure consistency, this commit also had to refactor and simplify inbound non-poll network request handling, though there should be no functional change.
IOSHttpResponse no longer exposes the Send() method, only classes in OpenSim.Framework.Servers.HttpServer should be doing this.
Only the GetTextureHandler was sending its own response.  Now it leaves this to BaseHttpServer, like all other core handlers.
2012-06-20 23:29:49 +01:00
Justin Clark-Casey (justincc) 6219c137e1 Fix very recent regression in 917d753 where I put the ++updatesThisCall outside the batching part of ProcessEntityUpdates()
This stopped any batching happening and since this method is called periodically updates were sent very slowly
2012-06-14 04:00:32 +01:00
Justin Clark-Casey (justincc) da3877a77d If we're going to discard a terse update block because it's now someone else's hud, then don't still add it to the list of blocks for the update message. 2012-06-14 04:00:22 +01:00
Justin Clark-Casey (justincc) 10ed7e3bbc correct wrong incomplete comment from previous commit 3c3ea19 in AttachmentsModule 2012-06-14 04:00:04 +01:00
Justin Clark-Casey (justincc) f248f8bf31 Fix a bug where scene objects attached as HUDs through scripts would not disappear for other avatars.
We do this by sending a kill message for that object to all other avatars apart from the one that has the hud.
2012-06-14 03:59:56 +01:00
Justin Clark-Casey (justincc) 6f2031001b Fix a race condition where an object update for a hud could be sent to non-owner avatars if the hud was attached directly from within the region.
If this happens, then the non-owners would see unremovable huds that they did not own until relog, and sometimes even beyond that.
This was due to a race between the entity update and the attachment code when moving an object from within scene to a hud.
2012-06-14 03:59:46 +01:00
Justin Clark-Casey (justincc) 1c5ad8e9ab Add SOG.HasPrivateAttachmentPoint to tell if a SOG has a private attachment point. HUDs attachment points are private.
Change SOP.SendFullUpdateToClient() and SoundModule.PlayAttachedSound() to use this rather than different magic number formulations.
This also corrects a bug in PlayAttachedSound() where the code assumed that all attachment points over 30 were HUDs.
It appears this is no longer true with Neck and Root (Avatar Center)
2012-06-14 03:59:39 +01:00
Justin Clark-Casey (justincc) d8d8b8fc9b minor: remove unnecessary IsAttachment = false setting for new object in UploadObjectAssetModule, property always starts as false 2012-06-14 03:59:09 +01:00
Justin Clark-Casey (justincc) 719efdaf1f minor: refactor part of LLClientView.ProcessEntityUpdates() to remove duplicate code 2012-06-14 03:58:54 +01:00
Justin Clark-Casey (justincc) 6a8e3907ca Remove long obsolete and unused IClientAPI.KillEndDone() 2012-06-14 03:18:23 +01:00
Justin Clark-Casey (justincc) 5a1b8fc6f7 Add ObjectUpdate as one of the packets that can be screened out when setting debug packet level 2012-06-14 03:18:15 +01:00
Justin Clark-Casey (justincc) 00ac962db7 In the osGetGrid functions, if the [GridInfo] section does not exist then return "Configuration Error", as already happens if there is no GridInfoURI 2012-06-14 03:18:08 +01:00
Justin Clark-Casey (justincc) cb518ad68c Add "deregister region" by uuid command to grid service to allow manual deregistration of simulators.
Useful if a simulator has crashed without removing its regions and those regions have been reconfigured differently
2012-06-14 03:18:00 +01:00
Justin Clark-Casey (justincc) 4859bc8c49 Remove accidental timeout left in during earlier debugging. Has been in since two commits ago (b099f26) 2012-06-14 03:13:55 +01:00
Justin Clark-Casey (justincc) 196e014782 OnConnectionClosed listeners, retrieve data from IClientAPI.SceneAgent rather than scanning all scene for the presence with the right id
Stop checking IsLoggingOut on these listeners, if called with a root agent then we always want to perform these actions.
This covers cases where the client is closed due to manual kick, simulator shutdown, etc.
2012-06-14 03:13:37 +01:00
Justin Clark-Casey (justincc) c6ffaaa959 Set IClientAPI.IsActive = false early on client removal due to ack timeout rather than using IsLoggingOut flag.
IsActive is more appropriate since unack timeout is not due to voluntary logout.
This is in line with operations such as manual kick that do not set the IsLoggingOut flag.
It's also slightly better race-wise since it reduces the chance of this operation clashing with another reason for client deactivation (e.g. manual kick).
2012-06-14 03:08:08 +01:00
Justin Clark-Casey (justincc) c3104f4bd2 If the simulator closes a root agent due to ack timeout, then send the client a kick message with that reason, in case it is somehow still listening. 2012-06-14 03:07:32 +01:00
Justin Clark-Casey (justincc) d8c40ca462 In PresenceDetector.OnConnectionClose(), use the IsChildAgent check already available on IClientAPI.SceneAgent rather than retrieving it again by scanning all scenes. 2012-06-14 03:07:25 +01:00
Justin Clark-Casey (justincc) 48f47bb4c7 Comment out the scene presence sitting debug log messages for now 2012-06-14 03:04:09 +01:00
Justin Clark-Casey (justincc) 8db6edbe87 Set CreateDefaultAvatarEntries = true in Robust.HG.ini.example to match Robust.HG.ini
Thanks to Ai Austin for the spot.
2012-06-14 03:04:02 +01:00
Justin Clark-Casey (justincc) a57b78b44b Scale down per frame MS stats to match scaled simulator FPS stat.
This makes frame time stats properly tally with fps, which saves confusion and makes it easier to interpret numbers.
In some ways this is not so artifical - physics FPS runs at the higher rate.
2012-06-14 03:03:26 +01:00
Justin Clark-Casey (justincc) 280d005d55 Start sending spare frame time MS viewer stat. Make frame time correctly display total frame time, not just non-spare time.
This makes it easier to see when components of frame time exceed normal permitted frame time.
Currently reflect scene frame times.
2012-06-14 03:03:01 +01:00
Justin Clark-Casey (justincc) 61e7d4a0e2 Properly show per frame millisecond statistics per frame, not as amount of time taken per second.
This is to make these statistics actually match their names (and also be more accurate as number of frames can vary under heavy load)
Currently using scene frames (11.23 every second) instead of physics frames (56.18 per second)
2012-06-14 02:51:43 +01:00
Justin Clark-Casey (justincc) 3e75083d2d Add last frame time monitor to MonitorModule now that this value is useful 2012-06-14 02:51:17 +01:00
Justin Clark-Casey (justincc) 3d400dd677 Create avatar entries necessary to stop new v3 avatars being clouds (pants, shape, etc.) by default in grid mode.
This only affects avatars created through the "create user" console command or createuser XMLRPC.
This matches the default setting for standalone
2012-06-14 02:50:59 +01:00
Justin Clark-Casey (justincc) 1fbb0f97bf Add documentation to AllowGodFunctions setting in [LL-Functions] 2012-06-14 02:50:43 +01:00
Justin Clark-Casey (justincc) 4968191c1e Fix regression in 5f4f9f0 (Fri Jun 8 2012) which stopped "show stats" and json stats from working 2012-06-14 02:50:36 +01:00
Justin Clark-Casey (justincc) 8889309324 Stop sending a DisableSimulator packet in LLClientView.Close(), which is a duplicate for child agents and unnecessary for root agents.
Close() already calls Scene.RemoveClient() which sends the right eq or udp DisableSimulator message to child agents.
2012-06-14 02:50:28 +01:00
Justin Clark-Casey (justincc) 27a7ba3e6a Instead of retrieving the known client again in LLUDPServer.RemoveClient(), check the IsLoggingOut flag instead.
This is slightly better thread-race wise
2012-06-14 02:50:17 +01:00
Justin Clark-Casey (justincc) 2d16d14ef1 If logging a client out due to ack timeout, do this asynchronously rather than synchronously on the outgoing packet loop.
This is the same async behaviour as normal logouts.
This is necessary because the event queue will sleep the thread for 5 seconds on an ack timeout logout as the client isn't around to pick up the final event queue messages.
2012-06-14 02:50:09 +01:00
Justin Clark-Casey (justincc) f2f8dcd65c Add regression test for client logout due to ack timeout. 2012-06-14 02:50:02 +01:00
Justin Clark-Casey (justincc) 332f8b6623 Remove null checks at top of LLUDPServer.ProcessInPacket(). Neither packet nor client are ever null. 2012-06-14 02:49:55 +01:00
Justin Clark-Casey (justincc) 4eda679e12 Store already retrieve IClientAPI in IncomingPacket structure for later use rather than doing another retrieve on dequeue.
Instead of checking whether the client still exists by trying to retrieve again from the client manager, this patch gets it back from IncomingPacket and checks the IClientAPI.IsActive state.
2012-06-14 02:49:39 +01:00
Justin Clark-Casey (justincc) 498154af80 Don't make duplicate call to ScenePresence.Close() separately in ETM.DoTeleport() if an agent needs closing.
This is always done as part of Scene.RemoveClient()
Also refactors try/catching in Scene.RemoveClient() to log NREs instead of silently discarding, since these are useful symptoms of problems.
2012-06-14 02:49:31 +01:00
Justin Clark-Casey (justincc) ba0ebe6d75 Go back to calling IncomingCloseAgent() in the "kick user" command for consistency instead of IClientAPI.Close() directly.
This no longer double counts child agent removals
2012-06-14 02:49:22 +01:00
Justin Clark-Casey (justincc) 28f93512bc Remove duplicate update of user count in Scene.IncomingCloseAgent()
This is already done in Scene.RemoveClient() which IncomingCloseAgent() always ends up calling.
2012-06-14 02:49:14 +01:00
Justin Clark-Casey (justincc) 78c2ef2346 Fix bug with "kick user" reducing agent counts by 2 instead of 1.
This is done by making the kick user command call IClientAPI.Close() rather than routing through Scene.IncomingCloseAgent(), which also called IClientAPI.Close()
DisableSimulator for child agents is moved from IncomingCloseAgent() to RemoveClient(), this is not a functional change since IncomingCloseAgent() always ends up calling RemoveClient()
2012-06-14 02:49:05 +01:00
Justin Clark-Casey (justincc) d30d68657e Record the fact that child agents can have asset transactions.
Also change code to grab the agent asset transaction module once.
2012-06-14 02:48:56 +01:00
Justin Clark-Casey (justincc) 3a27f656b3 Don't send kill object messages to clients when a child agent is closed. 2012-06-14 02:48:48 +01:00
Justin Clark-Casey (justincc) cc27a6cb84 Log warning if we try to remove a UDP client that has already been removed. 2012-06-14 02:48:36 +01:00
Justin Clark-Casey (justincc) ed21576ce0 Allow the thread watchdog to accept an alarm method that is invoked if the timeout is breached.
This alarm can then invoke this to log extra information.
This is used in LLUDPServer to show which client was being processed when incoming and outgoing udp watchdog alarms are triggered.
2012-06-14 02:48:29 +01:00
Talun 9d9e042b4c Mantis 4597 AgentPaused packet is ignored.
The packet was actually being handled but not acted on.
This change extends the default timeout for paused clients to 5 minutes
and makes both the paused and non-paused timeout periods configurable.
2012-06-14 02:47:21 +01:00
Justin Clark-Casey (justincc) 4fb8d5cfba Remove unused ScenePresence list structure in llGetAgentList() 2012-06-06 01:40:45 +01:00
Justin Clark-Casey (justincc) 896c4b6248 Fix build break whree accidentally did inv.Folders rather than inv.Folders.Count in a minor change. 2012-06-04 21:17:43 +01:00
Justin Clark-Casey (justincc) f6730da13a minor: tidy up some comments 2012-06-04 21:17:23 +01:00
Justin Clark-Casey (justincc) 41d98916df Fix various issues with http inventory
1) The return messages were being wrongly populated with the names of asset, inventory and sale types when their corresponding integers should have been used instead.
2) Folders with links were including the linked items in the descendents figure, when only the links should be included.
3) Links and linked items in link folders were not being included in the return data, and not in the correct order.

Now that these issues have been addressed, outfits and attachments appear to work consistently when HTTP inventory is enabled (as is now the default).
2012-06-04 21:17:15 +01:00
Justin Clark-Casey (justincc) 771539a4e0 Instead of updating sim stats root agent, child, objects and scripts accounts every single scene frame, update in the once every 3 seconds SimStatsReporter run 2012-06-04 21:16:46 +01:00
Justin Clark-Casey (justincc) 27c62bba99 Add optional total avatars, total prims and active prims stats to ODE plugin.
These will act as a sanity check with the main scene stats, to show that physics scene entities are being managed properly.
Total prims will not match scene total prims since physics total does not include phantom prims
2012-06-04 21:08:28 +01:00
Justin Clark-Casey (justincc) 93e053a122 If OdeScene.Near() returns no collision contacts, then exit as early as possible. All subsequent code is only relevant if there are contacts. 2012-06-04 21:08:21 +01:00
Justin Clark-Casey (justincc) eb022f4cc1 Add optional stat for the other collision time per frame not spent in ODE native spaces or geom collision code 2012-06-04 21:08:12 +01:00
Justin Clark-Casey (justincc) 83542034dd Add avatar forces calculation, prim force and raycasting per frame millisecond optional stats 2012-06-04 21:08:02 +01:00
Justin Clark-Casey (justincc) 627382f702 Collection optional avatar and prim taint frame millisecond times 2012-06-04 21:07:51 +01:00
Justin Clark-Casey (justincc) 2eb563b3bb Remove recent optional native collision frame milliseconds stat
Unnecessary since this has now been broken down into space collisions and geom collisions
2012-06-04 21:07:41 +01:00
Justin Clark-Casey (justincc) 54a23f14d5 Add optional stat that records milliseconds spent notifying collision listeners in physics frames 2012-06-04 21:07:32 +01:00
Justin Clark-Casey (justincc) e8059b74f8 Add avatar and prim update milliseconds per frame optional stats 2012-06-04 21:07:23 +01:00
Justin Clark-Casey (justincc) 075909520a Add option native step frame ms stat 2012-06-04 21:07:16 +01:00
Justin Clark-Casey (justincc) 808bf12cd5 Add total ODE frame time optional stat, as a sanity check on the main scene physics stat 2012-06-04 21:07:07 +01:00
Justin Clark-Casey (justincc) 0f39f41317 Break down native ODE collision frame time stat into native space collision and geom collision stats 2012-06-04 21:07:00 +01:00
Justin Clark-Casey (justincc) f23b7ae3e9 Rename new collision stats to 'contacts' - there are/can be multiple contacts per collision and this is what is actually being measured. 2012-06-04 21:06:52 +01:00
Justin Clark-Casey (justincc) 5e4b09fc22 Stop adding an unnecessary duplicate _perloopcontact if the avatar is standing on a prim.
This has already been added earlier on in the method.
2012-06-04 21:06:42 +01:00
Justin Clark-Casey (justincc) 25ab7841b7 minor: comment out currently unused OdeScene.sCollisionData 2012-06-04 21:06:32 +01:00
Justin Clark-Casey (justincc) 0c0e575379 Add ODE avatar and prim collision numbers if extra stats collection is enabled. 2012-06-04 21:06:23 +01:00
Justin Clark-Casey (justincc) 68946bffae Fix OdeScene.GetTopColliders() to return the top 25 colliders rather than the first 25 that had non-zero collision scores.
Also zeros collisions scores on all prims after report collection, not just the top 25.
As before, this collision scores are only reset after a report is requested, which may give unrealistic numbers on the first request.
So to see more realistic scores, ignore the first report and then refresh the request after a couple of seconds or so.
2012-06-04 21:06:13 +01:00
Justin Clark-Casey (justincc) 2fc461d9ab Add an optional mechanism for physics modules to collect and return arbitrary stats.
If active, the physics module can return arbitrary stat counters that can be seen via the MonitoringModule
(http://opensimulator.org/wiki/Monitoring_Module)
This is only active in OdeScene if collect_stats = true in [ODEPhysicsSettings].
This patch allows OdeScene to collect elapsed time information for calls to the ODE native collision methods to assess what proportion of time this takes compared to total physics processing.
This data is returned as ODENativeCollisionFrameMS in the monitoring module, updated every 3 seconds.
The performance effect of collecting stats is probably extremely minor, dwarfed by the rest of the physics code.
2012-06-04 21:05:57 +01:00
Justin Clark-Casey (justincc) 59a48d9ebb Add console command "teleport user" to allow teleport from the region console
See "help teleport user" on the console for more details
2012-05-31 02:28:30 +01:00
Justin Clark-Casey (justincc) 533d1ea20c refactor: factor out entity transfer state machine into a separate class to make code more analyzable 2012-05-31 02:28:20 +01:00
Justin Clark-Casey (justincc) dca1ca1d07 If handling the failure of teleport, move agent state to CleaningUp when we start the handling.
Also fixes the log warning from ResetInTransit() if the state is cleared direct from Transferring or ReceiveAtDestination, as pointed out in mantis 5426
2012-05-31 02:25:07 +01:00
Justin Clark-Casey (justincc) 20a3907e86 If restating a region, clean up the physics scene after the main scene has been closed not before.
If this is done before then on ODE agent update calls still incoming can fail as they try to use a raycastmanager that has been disposed.
Bullet plugin does nothing on Dispose()
However, I wouldn't be at all surprised if individual region restarting was buggy in lots of other areas.
2012-05-31 02:24:36 +01:00
Justin Clark-Casey (justincc) 7358e5748d Add Blake/Techplex to CONTRIBUTORS. Thanks! 2012-05-31 02:24:31 +01:00
Blake.Bourque a1b64db942 One can now get hyoergrid region co-ordinates with llRequestSimulatorData 2012-05-31 02:23:00 +01:00
Justin Clark-Casey (justincc) 4d44f2d248 Use GetInventoryItem() in LSL_Api.InventoryKey(string name, int type).
Also removes small bug where calling this method would add 1 to LPS, evne though all callers already did this.
2012-05-31 02:22:53 +01:00
Justin Clark-Casey (justincc) 67abbcf269 Use SceneObjectPartInventory.GetInventoryItem() in OSSL.AvatarStopAnimation instead of searching the task inventory manually. 2012-05-31 02:22:45 +01:00
Justin Clark-Casey (justincc) 6b819a9032 refactor: replace LSL_Api.InventoryKey(string) largely with SceneObjectPartInventory.GetInventoryItem(string)
Also gets llStopAnimation() to call KeyOrName rather than duplicating logic.
2012-05-31 02:22:27 +01:00
Talun 2021a8aedb Mantis 6028 osAvatarStopAnimation not stopping animations via UUID
Corrected to stop animations using the animation UUID similar to llStopAnimation.
See http://opensimulator.org/wiki/OsAvatarStopAnimation
2012-05-31 02:18:55 +01:00
Justin Clark-Casey (justincc) 58dc175ae3 on agent cross, remove from physics scene after its been placed in transit, not before. 2012-05-31 02:18:27 +01:00
Justin Clark-Casey (justincc) 4ad6763956 refactor: make ETM.CrossAgentToNewRegionAsync neighbourRegion == null check return earlier to simplify method 2012-05-31 02:18:20 +01:00
Justin Clark-Casey (justincc) 702826b850 Fix bug where a failed QueryAccess to a remove region would always have the reason "Communications failure" no matter what the destination region actually returned 2012-05-31 02:18:11 +01:00
Justin Clark-Casey (justincc) 8d30a1f74b Stop it being possible for an agent to teleport back to its source region before the source region has finished cleaning up old agent data and structures.
If this is allowed, then the client usually gets forcibly logged out and data structures might be put into bad states.
To prevent this, the binary state machine of EMT.m_agentsInTransit is replaced with a 4 state machine (Preparing, Transferring, ReceivedAtDestination, CleaningUp).
This is necessary because the source region needs to know when the destination region has received the user but a teleport back cannot happen until the source region has cleaned up.
Tested on standalone, grid and with v1 and v3 clients.
2012-05-31 02:18:03 +01:00
Justin Clark-Casey (justincc) 1a988ba835 In remote QueryAccess, also receive the actual status (true|false) instead of always true no matter what the callee actually returned.
This was due to two things
1) SimulationServiceConnector.QueryAccess was always looking to the outer result["success"].
But if a "_Result" map is returned (which is certainly the case right now), then the true success is _Result["success"], result["success"] is always true no matter what
2) If QueryAccess was false at the destination, then AgentHandlers.DoQueryAccess() was never putting this in the result.
The default action of SerializeJsonString() is not to put false booleans in the JSON!!!, so this has to be explicitly set.
2012-05-31 02:17:55 +01:00
Justin Clark-Casey (justincc) c422f852a6 Don't actually proceed on a within-region teleport if another is already taking place, rather than just (falsely) logging that we're not going to proceed.
An oversight from recent commit 9ab0c81
2012-05-31 02:17:49 +01:00
Justin Clark-Casey (justincc) bcacdb3352 On inter-region teleport, only stand the avatar up if the QueryAccess call to the destination scene actually succeeds. 2012-05-31 02:17:42 +01:00
Justin Clark-Casey (justincc) cd61567de8 Now that the EntityTransferModule is per-region, fetch the event queue module once rather than repeatedly via scene presences 2012-05-31 02:17:35 +01:00
Justin Clark-Casey (justincc) 5c48c3c57a Fix issue where a dns resolution failure on the final destination might leave the user unable to teleport since the transit flag was not being reset.
This moves the 'already in transit' check further up and resets the flag if dns resolution fails and in the new required places.
2012-05-31 02:17:28 +01:00
Justin Clark-Casey (justincc) 7692ccc0cc Make ISimulationScene.GetScene() used the more efficient region id for lookup rather than the region handle. 2012-05-31 02:17:18 +01:00
Justin Clark-Casey (justincc) 17cc7e85e2 If an agent is still registered as 'in transit' by the source region, don't allow an immediate teleport back.
This is to help relieve a race condition when an agent teleports then immediately attempts to teleport back before the source region has properly cleaned up/demoted the old ScenePresence.
This is rare in viewers but much more possible via scripting or region module.
However, more needs to be done since virtually all clean up happens after the transit flag is cleared .
Possibly need to add a 'cleaning up' state to in transit.
This change required making the EntityTransferModule and HGEntityTransferModule per-region rather than shared, in order to allow separate transit lists.
Changes were also required in LocalSimulationConnector.
Tested in standalone, grid and with local and remote region crossings with attachments.
2012-05-31 02:16:52 +01:00
Justin Clark-Casey (justincc) 96b3e1d0fa Don't eagerly clear frame collision events when physics actors subscribe and unsubscribe from collisions, in order to avoid a race condition.
Since this is done directly from ScenePresence, it can lead to a race condition with the simulator loop.
There's no real point doing it anyway since the clear will be done very shortly afterwards by the simulate loop and either there are no events (for a new avatar) or events don't matter (for a departing avatar).
This matches existing behaviour in OdePrim
2012-05-31 02:11:45 +01:00
Justin Clark-Casey (justincc) dc3cfcbe69 Check agent limit against root agent count rather than both root and child agents
From sl docs such as http://community.secondlife.com/t5/English-Knowledge-Base/Managing-Private-Regions/ta-p/700115
agent should apply to avatars only.
This makes sense from a user perspective, and also from a code perspective since child agents with no physics or actions take up a fraction of root agent resources.
As such, the check is now only performed in Scene.QueryAccess() - cross and teleport check this before allowing an agent to translocate.
This also removes an off-by-one error that could occur in certain circumstances on teleport when a new child agent was double counted when a pre-teleport agent update was performed.
This does not affect an existing bug where limits or other QueryAccess() checks are not applied to avatars logging directly into a region.
2012-05-31 02:05:59 +01:00
Justin Clark-Casey (justincc) 1ca1f80eac Remove a call stack debugging line accidentally left in from a few days ago at SceneObjectPartInventory.ApplyNextOwnerPermissions(). 2012-05-25 02:56:06 +01:00
Justin Clark-Casey (justincc) afe2b437bc minor: Change [OBJECT COMMANDS MODULE] log strings to [REGION COMMANDS MODULE] strings, though all these are currently commented out anyway 2012-05-24 01:24:24 +01:00
Justin Clark-Casey (justincc) dff71c1aa9 Add "show scene" command which lists stats for the currently selected console scene(s)
This includes prim count, script count, avatar count, etc.
Information is currently the same as "show stats", though show stats can only show one scene at a time because it listens for the latest outgoing stats packet (a bad approach that needs to change).
Might be better to tie this module into the other stats module to display arbitrary stats rather than fetching directly from scene.SimStatsReporter.
Console command is "show scene" because "show region" already exists for the grid service, which is unfortunate.
Might need to make a distinction between "scene" relating to a live scene and "region" relating to more static region data (url, coords, etc.)
2012-05-24 01:24:12 +01:00
Justin Clark-Casey (justincc) 7f9a025e30 refactor: Rename ConsoleTableRow and ConsoleTableColumn to ConsoleDisplayTableRow and ConsoleDisplayTableColumn 2012-05-24 01:24:06 +01:00
Justin Clark-Casey (justincc) a0ac284a11 Add ConsoleDisplayList for more consistent formatting of console output in list form.
Convert "show region" to use this structure rather than hand-constructing
2012-05-24 01:23:57 +01:00
Justin Clark-Casey (justincc) 5341036261 refactor: rename ConsoleTable -> ConsoleDisplayTable for clarity 2012-05-24 01:23:50 +01:00
Justin Clark-Casey (justincc) 46a6cab307 Add missing Y co-ord in "show region" console command information 2012-05-24 01:23:43 +01:00
Justin Clark-Casey (justincc) edb17d1aac Lay out "show region" information in an easier to read line by line format 2012-05-24 01:23:34 +01:00
Justin Clark-Casey (justincc) dd05e96066 Fetch the dialog module reference in AttachmentsModule in RegionLoaded() not AddRegion()
The reference is not guaranteed to be there when AddRegion() is called but will definitely be present at RegionLoaded() if it's going to be present at all.
2012-05-24 01:23:27 +01:00
Justin Clark-Casey (justincc) ed0878ca23 minor: Make log class names in InventoryAccessModule uniform 2012-05-24 01:23:20 +01:00
Justin Clark-Casey (justincc) 54c222be26 Fix bug where an avatar that had an object they owned attached through llAttachToAvatar() or osForceAttachToAvatar() would wrongly have next permissions come into play when they detached that object and rezzed it in scene.
This is because the attachments module code was setting the 'object slam' bit by using PermissionMask.All
Solution here is to route the attachment item creation call through the existing inventory code in BasicInventoryAccessModule rather than copy/pasted code in AttachmentsModule itself.
2012-05-24 01:23:09 +01:00
Justin Clark-Casey (justincc) 80d139297a Setting 'in transit' on a local teleport as well as inter-region teleports.
This is to eliminate possible race conditions if two teleport calls are made concurrently, where at least one is a local teleport.
This is pretty much impossible on a manual user teleport but can happen on script-invoked teleports.
2012-05-24 01:23:03 +01:00
Justin Clark-Casey (justincc) 8e6459f616 minor: extend commented out LinkInventoryItem log message for future use 2012-05-24 01:22:48 +01:00
Justin Clark-Casey (justincc) ba909cb692 Fix bug where outfit folders could not be renamed.
Outfit folders are a type of system folder whose details are allowed to change.
2012-05-24 01:19:53 +01:00
Justin Clark-Casey (justincc) 96b964f7fa Enable FetchInventoryDescendents2 and FetchInventory2 caps by default. This appears to be required now for LL 3.3.1 to work properly.
Without this, LL 3.3.1 continually pushes LLInventoryModelFetchDescendentsResponder::error 499 to its log.
This cap will be ignored by older viewers - UDP inventory will work normally.
2012-05-24 01:15:04 +01:00
Justin Clark-Casey (justincc) 7bbab2f1d5 Fix issue where a new outfit folder is not created when a new outfit is saved if there are no previous outfits
This was because AddFolder() was disallowing these though they are legal.
2012-05-24 01:14:44 +01:00
Justin Clark-Casey (justincc) e5f3af4abe Fix build break. Comment out EQG deregister/register logging. 2012-05-24 01:10:33 +01:00
Justin Clark-Casey (justincc) 452538c6b0 Add millisecond logging to pCampBot for debugging purposes 2012-05-24 01:10:24 +01:00
Justin Clark-Casey (justincc) 5c828724f3 Add level 2 debug eq logging which logs event queue polls.
Refactor: eq message logging into common method.
2012-05-24 01:10:15 +01:00
Justin Clark-Casey (justincc) 6538dd24bb Invoke log4net configurator in pCampBot.exe in order to get OpenSim sylte logging 2012-05-24 01:10:09 +01:00
Justin Clark-Casey (justincc) 996abe1ea5 refactor: move EventQueueGet path generation into common method. Rename some local variables in line with code conventions. Add commented out EQG log lines for future use. 2012-05-24 01:09:58 +01:00
Justin Clark-Casey (justincc) f421e0c3d1 Don't eagerly clear frame collision events when physics actors subscribe and unsubscribe from collisions, in order to avoid a race condition.
Since this is done directly from ScenePresence, it can lead to a race condition with the simulator loop.
There's no real point doing it anyway since the clear will be done very shortly afterwards by the simulate loop and either there are no events (for a new avatar) or events don't matter (for a departing avatar).
This matches existing behaviour in OdePrim
2012-05-24 01:09:46 +01:00
Justin Clark-Casey (justincc) e4eaca5f9b minor: improve method doc for TestSameSimulatorSeparatedRegionsCreateAgentFails() 2012-05-24 01:08:32 +01:00
Justin Clark-Casey (justincc) ef5925fa18 Add regression TestSameSimulatorSeparatedRegionsCreateAgentFails() 2012-05-24 01:08:30 +01:00
Justin Clark-Casey (justincc) bae48a9394 minor: Remove redundant EstateOwner != UUID.Zero check in IsAdministrator because checking EstateOwner == user
Due to an earlier check we already know that user != UUID.Zero so if EstateOwner == UUID.Zero, EstateOwner == user can never be true
2012-05-24 01:06:47 +01:00
Justin Clark-Casey (justincc) c813ed44d8 Comment out TestSameSimulatorSeparatedRegionsQueryAccessFails() regression test logging accidentally left in 2012-05-17 01:52:15 +01:00
Justin Clark-Casey (justincc) 885bec68bd Add regression TestSameSimulatorSeparatedRegionsQueryAccessFails() 2012-05-17 01:52:06 +01:00
Justin Clark-Casey (justincc) b553a05db3 Remove redundant "Teleport failed:" from reason when QueryAccess fails for the destination simulator. This part of the string is already provided by the viewer.
Also adds more reason logging for diagnostics when teleports are refused/fail.
2012-05-17 01:51:59 +01:00
Justin Clark-Casey (justincc) 5473c4f8cc Route OAR SOG loading through the common SceneObjectSerializer.FromXml2Format() rather than the functionally identical but buggy Xml2ToSOG().
Remove buggy Xml2ToSOG().
2012-05-17 01:51:48 +01:00
Justin Clark-Casey (justincc) 0d73f81fb5 Fix issue where loading OARs could sometimes result in link numbers being reordered.
This was because the parts in scene objects were sometimes not serialized in link order.
This is perfectly fine since the parts still have the right link numbers, but an extra fix to adjust for this
had not been done in the SerialiserModule methods that OAR loading used.
Add regression test for same.
Addresses http://opensimulator.org/mantis/view.php?id=5948, http://opensimulator.org/mantis/view.php?id=5749
2012-05-17 01:51:39 +01:00
Justin Clark-Casey (justincc) 66c204b983 Allow use of regular expressions in "show object name", "show part name" and "delete object name" console commands if --regex switch is used.
Deleteing objects by name, creator uuid or owner uuid now requires confirmation to avoid accidental deletion.
2012-05-17 01:51:32 +01:00
Justin Clark-Casey (justincc) afb0600621 minor: add explanative comment to 'missing baked texture' logging commonly seen on inter-simulator teleports where avatar baked textures are not available from the asset service. 2012-05-17 01:50:52 +01:00
Justin Clark-Casey (justincc) a13f2c6985 minor: comment out individual attachment transfer log messages for now 2012-05-17 01:50:45 +01:00
Justin Clark-Casey (justincc) 17c7ef06ba Set the agent in transit teleport flag at the first available opportunity (i.e. when IsInTransit() was being checked) to close down a race condition.
On EntityTransferModule.DoTeleport() there was an IsInTransit() check to prevent multiple simultaneous teleport attempts.
However, the SetInTransit() was only performed later on, which left a window in which multiple threads could pass the IsInTransit() check.
This has been seen in the field and the results aren't pretty.
This commit effectively combines the IsInTransit() and SetInTransit() checks so there is no such window.
More failure cases are made to to call ResetInTransit() to adjust to this move.
2012-05-17 01:50:35 +01:00
Justin Clark-Casey (justincc) 4933ce49b6 Add more region information to some teleport related logging 2012-05-17 01:50:27 +01:00
Justin Clark-Casey (justincc) 5cec1fa50a Fix mono compiler warning.
Last jenkins failure looked like a glitch.
2012-05-17 01:50:14 +01:00
Justin Clark-Casey (justincc) debd83b06a Print out more information on connecting bots 2012-05-17 01:50:02 +01:00
Justin Clark-Casey (justincc) f01618ad1a Stagger multiple bot logins by 5 seconds to make this part of the test more 'realistic'
TODO: Need to make this value configurable by a command line parameter to pCampbot
2012-05-17 01:49:48 +01:00
Justin Clark-Casey (justincc) cfc1dba99b Do bot startup on another thread so console is responsive during this process 2012-05-17 01:49:39 +01:00
Justin Clark-Casey (justincc) 529a3f2400 Do each bot shutdown on its own threads to prevent one slow shutdown holding up all the rest.
This does increase the aggressiveness of shutdown
Also prevents the bot list being locked for a long period, which was preventing commands such as "show bots" from working during shutdown
2012-05-17 01:49:31 +01:00
Justin Clark-Casey (justincc) 7c1abc5225 Increase minimum period between bot actions to 3 seconds, so that teleport doesn't fall under the minimum 2 second limits that clients take to process it 2012-05-17 01:49:15 +01:00
Justin Clark-Casey (justincc) ca22b5e2f0 Change bot.IsConnected to be ConnectionState with Disconnected, Connecting, Connnected and Disconnecting states 2012-05-17 01:48:47 +01:00
Justin Clark-Casey (justincc) 9ec74f2098 Provide feedback on bot login states in pCampbot 2012-05-17 01:48:45 +01:00
Justin Clark-Casey (justincc) cb4ae39cb9 If a bot is not connected, show region name "(none)" instead of throwing an exception in the "show bots" command of pCampbot 2012-05-17 01:46:55 +01:00
Justin Clark-Casey (justincc) 802488814f Add ConsoleTable framework class for future uniform formatting of console output tables.
Still subject to change - if you use this be prepared to change your output code if/when the methods change.
Make new "attachments show" command use this.
2012-05-17 01:46:46 +01:00
Justin Clark-Casey (justincc) 53aa48b42c Add "attachments" show console command that will show the server's record of which attachments an in-scene avatar has.
For debugging purposes.
2012-05-17 01:46:36 +01:00
Justin Clark-Casey (justincc) b33801f854 Improve logging on the prim inventory script asset request path for future use.
This adds name and description of the request handler to http request logging when DebugLevel >= 1
2012-05-17 01:45:51 +01:00
Talun fc88ce0615 Mantis 6015 new LSL function llGetAgentList.
Details in the lsl wiki
2012-05-17 01:45:43 +01:00
Justin Clark-Casey (justincc) 28daec7f4e Remove physics actor related race conditions in SetVehicleFlags() and SetPhysicsAxisRotation()
sop.PhysActor can currently become null at any time.
2012-05-17 01:45:35 +01:00
Justin Clark-Casey (justincc) f25efa291d Add automated TestllBreakLink() 2012-05-17 01:45:24 +01:00
Justin Clark-Casey (justincc) 5ed0559cf2 Add automated TestllCreateLink() 2012-05-17 01:45:13 +01:00
Justin Clark-Casey (justincc) 8c1e549c12 refactor: Eliminate local id parameter from api initialize.
This is always available from m_host.LocalId
2012-05-17 01:44:59 +01:00
Justin Clark-Casey (justincc) 796334c5ff Instead of constantly looking up unchanging self item in script code, pass in self item on initialization. 2012-05-17 01:44:21 +01:00
Justin Clark-Casey (justincc) 2e1c2e1261 Escape and unescape xml element names if necessary in ServerUtils.BuildXmlData() and ParseElement()
If AvatarService appearance data is retrieved over the network, then ServerUtils was attempting to transfer names such as "Wearable 0:0" directly to xml element names, resulting in an exception.
Space is not valid in xml element names.  Neither is : in this case since the intention is not to namespace.  Using names directly as keys is not a good idea.
To get around this problem this patch escapes and unescapes the element names as appropriate.
This has no impact on existing xml (since it had to be valid in the first place) but allows AvatarService data to be used over the network.
Setting appearance (from simulator to AvatarService) did not suffer this problem since the values are passed in the query string which is already properly escaped.
2012-05-10 00:50:20 +01:00
Talun a896aac4bd Mantis 1456 same region teleport of a sitting avatar.
Region to region was fixed some time ago in EntityTransferModule.
This applies the same fix for same region teleports.
2012-05-10 00:50:00 +01:00
Justin Clark-Casey (justincc) 80030d3f15 Perform SceneGraph.DuplicateObject() under existing m_updateLock already used for link and delinking, in order to avoid race conditions.
DuplicateObject() relies on source object having correct link numbers for the duration of the dupe.
Both link and delink can change link numbers such that they are not consistent for short periods of time.
2012-05-10 00:49:37 +01:00
Oren Hurvitz 4eba4a37ed Log the full exception when errors occur in BaseHttpServer 2012-05-10 00:49:29 +01:00
Justin Clark-Casey (justincc) dd0858e204 For osGetGridNick(), osGetGridName(), osGetGridLoginURI() and osGetGridCustom(), try to read from the [GridInfoService] section on standalone rather than [GridInfo]
[GridInfoService] is the section that's actually in bin/config-include/StandaloneCommon.ini.example
2012-05-10 00:49:15 +01:00
Justin Clark-Casey (justincc) 25fa6ee699 refactor: Instead of performing a ScenePresence lookup twice over LocateClientObject() and GetClientScene(), do the lookup just once in LocateClientObject() 2012-05-10 00:49:08 +01:00
Justin Clark-Casey (justincc) 84dfffe0aa Fix a bug in FriendsModule.StatusNotify() where all subsequent friends would not be notified once a non-local friend was found. 2012-05-10 00:48:57 +01:00
Justin Clark-Casey (justincc) aba803c447 Change LongCallTime on WebUtil to 3000, to match the time where request handling is considered "slow".
This may be the wrong thing to do but stops lots of log spam in HG setups now that the monitoring is extended to other outgoing calls.
LongCallTime may need to be made configurable.
2012-05-10 00:47:59 +01:00
Justin Clark-Casey (justincc) 79aae63aff minor: Tweak BaseHttpServer message to make it clear that this relates to slow handling of inbound requests. 2012-05-10 00:47:43 +01:00
Justin Clark-Casey (justincc) 59e93b8ee3 Extend 'slow' request logging to other server outbound requests (forms, rest, async rest) as well as the existing logging on outbound OSD requests.
Also prints out the first 100 chars of any slow request data since this can contain useful info (such as agent ID).
2012-05-10 00:47:26 +01:00
Justin Clark-Casey (justincc) e0e63f312f Reinsert a 2000ms delay before closing a no longer required agent on the source region after teleport to resolve Imprudence teleport problems.
Viewers 1 and 3 are fine with doing this immediately.  However, Imprudence has a small delay (<200ms, >500ms) after receiving the AgentCompleteMovement reply packet on the destination region before regarding that region as the currnet region.
If Imprudence receives a DisableSimulator in this period, it quits.
We are not restoring the full 5000ms delay since this brings back a bug where teleports permanently fail if an avatar tries to teleport back too quickly.
This commit also sends the AgentCompleteMovement packet to the client before telling the source region to release its old agent, in order to further cut down any possibility of the DisableSimulator being recieved before the AgentMovementComplete.
2012-05-10 00:39:57 +01:00
Snoopy Pfeffer d453372f4e Fixes Mantis #5999. llSetLinkPrimitiveParams with PRIM_BUMP_SHINY did cause a runtime error. 2012-05-10 00:39:50 +01:00
Justin Clark-Casey (justincc) 0f5a77c5bd Remove the somewhat misleading logging of the string length of some unknown requests, as this appeared to be some kind of numbered error code.
This brings these messages into line with similar messages that did not do this.
2012-05-10 00:39:43 +01:00
Justin Clark-Casey (justincc) 0c96d7ea5c minor: resolve some mono compiler warnings 2012-05-10 00:39:35 +01:00
Justin Clark-Casey (justincc) 4b947cd6d3 Implement optional name and description on http stream handlers so that we can relate a slow request to what the handler actually does and the agent it serves, if applicable.
This is most useful for capabilities where the url is not self-describing.
2012-05-10 00:38:26 +01:00
Justin Clark-Casey (justincc) ca586ca809 Comment out the five second sleep in etm.DoTeleport() if the old agent needs to be closed because it is no longer in the child's view distance.
This sleep appears unnecessary since a sleep has already occurred in WaitForCallback() whilst waiting for the destination region to notify of teleport success.
There are no async operations between this sleep and the WaitForCallback()
If this sleep is present, then teleporting back to the source region within 5 seconds results in a disconnection.
If this sleep is commented out then teleporting quickly back and forth between two simulators appears to work without issue.
Tested on standalone, local grid and distributed grid.
Please revert if there's something that I've missed.
2012-05-10 00:29:02 +01:00
Justin Clark-Casey (justincc) 880358f46b Remove some test code that accidentally crept in with 9d2e1c67 2012-05-10 00:28:53 +01:00
Justin Clark-Casey (justincc) 3393babb7d Add regression test for teleporting between neighbouring regions on the same simulator
This adds a non-advertised wait_for_callback option in [EntityTransfer].  Default is always true.
Teleport tests disable the wait for callback from the destination region in order to run within a single thread.
2012-05-10 00:28:47 +01:00
Justin Clark-Casey (justincc) c3ae90c067 Move max teleport distance check down into etm.DoTeleport() since this should apply to all teleport calls, not just those through Teleport() 2012-05-10 00:28:27 +01:00
Justin Clark-Casey (justincc) 25983f20a0 refactor: Split most of EntityTransferModule.Teleport() into its same region and different region teleport components.
DoTeleport() now retrives IEventQueue itself rather than requiring it to be passed in.
2012-05-10 00:25:25 +01:00
Justin Clark-Casey (justincc) 1441758bc6 Create TestHelpers.EnableLogging() and DisableLogging() to turn logging on and off within tests.
This makes *.Tests.dll.config files no longer needed, hence deleted.
2012-05-10 00:25:14 +01:00
Justin Clark-Casey (justincc) c7bbeb4490 Add request verb and url to error messages in WebUtil that lack this.
Make exception printing consistent across windows and mono.

Conflicts:

	OpenSim/Framework/WebUtil.cs
2012-05-10 00:21:09 +01:00
Diva Canto 6145f90423 Slight rewording of output messages. 2012-05-10 00:17:36 +01:00
Diva Canto 2e397d1514 HG: Moved User-level code down to the HGEntityTransferModule where it belongs. 2012-05-10 00:16:51 +01:00
Diva Canto a5d0a29dd9 Moved the inventory manipulation from HGEntityTransferModule to HGInventoryAccessModule where it belongs. They need to exchange some events, so added those to EventManager. Those events (TeleportStart and TeleportFail) are nice to have anyway. 2012-05-10 00:10:18 +01:00
Melanie de843fd0a8 Implement bulk inventory update over CAPS (not recursive by design,
do NOT CHANGE THIS, needed for HG 2.0)
2012-05-10 00:05:47 +01:00
Melanie ced4eeddcf Typo fix 2012-05-10 00:05:41 +01:00
Melanie 093910e90e Fix typos 2012-05-10 00:05:32 +01:00
Melanie 37685ec1b4 Start on Bulk inventory update via CAPS. Not functional yet. HG v2 2012-05-10 00:05:26 +01:00
Melanie ccd7d35b3f Add a corresponding method for items. HG v2 2012-05-10 00:05:19 +01:00
Melanie de7e0d7e52 Add SendRemoveInventoryFolders which allows to remove one or more
folders from the viewer's inventory view. For HG v2.0. More to come
2012-05-10 00:05:13 +01:00
Diva Canto 89ee03a24d HG: beginning of a more restrictive inventory access procedure (optional). Experimental: we'll try switching the root folder from under the viewer. 2012-05-10 00:02:14 +01:00
Melanie 79d1d3ca55 Commit the avination Teleport() methods (adaptedto justincc's changes) 2012-05-09 23:58:42 +01:00
Justin Clark-Casey (justincc) 675c208c7e zero out SP velocity before calling SP.Teleport(), as the client expects (though this is also effectively done by physics at the moment) 2012-05-09 23:58:35 +01:00
Justin Clark-Casey (justincc) b3307850ab refactor: Combine ScenePresence.Teleport() and TeleportWithMomentum()
These are identical apart from setting Velocity = zero, which has no practical effect anyway since this is zeroed when the avatar is added back to the physics scene.
2012-05-09 23:58:27 +01:00
Oren Hurvitz eaa840dbd9 OSSL: fixed the threat level check for osParseJSONNew 2012-05-09 23:57:49 +01:00
Justin Clark-Casey (justincc) f64089fa6c Restore _parent_scene.actor_name_map[prim_geom] = this; accidentally removed from ODEPrim.SetGeom.
This occurred in 7a574be3fd from Sat 21 Apr 2012.
This should fix collision detection.
Mnay thanks to tglion for the spot and the fix in http://opensimulator.org/mantis/view.php?id=5988
2012-05-09 23:57:27 +01:00
Justin Clark-Casey (justincc) 5157d2023d refactor: simply some properties code in BasicPhysicsPlugin 2012-05-09 23:52:41 +01:00
Justin Clark-Casey (justincc) 2cd927bb14 Fix bug where setting phantom on a prim would result in a server log message rather than setting phantom.
This was an oversight when removing some race conditions from PhysicsActor setting recently.
Regression tests extended to probe this code path.
Extending regression tests required implementation of a BasicPhysicsPrim (there was none before).  However, BasicPhysics plugin is still of no current practical use other than to fill in as a component for other parts of regression testing.
2012-05-09 23:52:04 +01:00
Justin Clark-Casey (justincc) 2889961622 Comment out spurious Body != IntPtr.Zero code after disableBody(), since disableBody() sets Body == IntPtr.Zero on all code paths. 2012-05-09 23:51:56 +01:00
Justin Clark-Casey (justincc) 232f59749e refactor: Simplify ODEPrim.AddChildPrim() by returning early where appropriate. 2012-05-09 23:51:48 +01:00
Justin Clark-Casey (justincc) d368a10cc7 Add test for setting physics in a linkset 2012-05-09 23:51:41 +01:00
Justin Clark-Casey (justincc) 139b848774 Add regression test for prim status when root prim in a new linkset is non-physical 2012-05-09 23:51:34 +01:00
Justin Clark-Casey (justincc) 74a5226af5 Fix a bug where linking a non-physical prim with a physical prim as root would make the non-physical prim phantom rather than part of the physics object.
On region restart, the whole object would become physical as expected.
Observed behaviour from elsewhere is that all prims in a new linkset should take on the status of the root prim.
Add regression test for this behaviour.
2012-05-09 23:51:25 +01:00
Justin Clark-Casey (justincc) c7ddc7a633 Remove redundant prim_geom != IntPtr.Zero checks in ODEPrim.
prim_geom == IntPtr.Zero only before a new add prim taint is processed (which is the first taint) or in operations such as scale change which are done in taint or under lock.
Therefore, we can remove these checks which were not consistently applied anyway.
If there is a genuine problem, better to see it quickly in a NullReferenceException than hide the bug.
2012-05-09 23:39:47 +01:00
Justin Clark-Casey (justincc) 265707d21c If a physical prim is manually moved (e.g. by a user) then set the geometry position as well as the body position
This is necessary to stop the moved prim snapping back to the original position on deselection if moved only once
This resolves http://opensimulator.org/mantis/view.php?id=5966
2012-05-09 23:39:37 +01:00
Justin Clark-Casey (justincc) eb39d1c4d4 Comment out debug [ASYNC DELETER] messages for now. 2012-04-30 19:29:17 +01:00
Justin Clark-Casey (justincc) 1197d48fc7 Remove mono compiler warning. Adjust message log to error from info 2012-04-30 19:29:08 +01:00
Justin Clark-Casey (justincc) 23e1a55ed5 Add text about using double quotes to surround console command arguments containing spaces to "help" text.
e.g. show object name "My long object name"
2012-04-30 19:29:01 +01:00
Justin Clark-Casey (justincc) ba2a539603 Put scene object related console commands into new "Objects" help category rather than "Regions" 2012-04-30 19:28:54 +01:00
Justin Clark-Casey (justincc) 8a8093d8dd Add flags information (phantom, physics, etc.) to "show object" and "show part" console commands 2012-04-30 19:28:41 +01:00
Justin Clark-Casey (justincc) 3fb0103452 Add regression test for teleporting an agent between separated regions on the same simulator.
This involves a large amount of change in test scene setup code to allow test scenes to share shared modules
SetupScene is now an instance method that requires an instantiation of SceneHelpers, though other SceneHelpers methods are still static
May split these out into separate classes in the future.
2012-04-30 19:27:21 +01:00
Justin Clark-Casey (justincc) aeefdaedc7 minor: make NPC tests run in a given order, comment out log lines in mock region data plugins, null out scene in script and npc torture tests, add other doc comments to torture tests 2012-04-30 19:18:54 +01:00
Justin Clark-Casey (justincc) 756d1f917f Stop individually deleting objects at the end of each ObjectTortureTest.
We can now do this since the entire scene and all objects within it are now successfully gc'd at the end of these tests.
This greatly improves the time taken to run each test (by reducing teardown time, not the time to actually do the test work that we're interested in).
Slightly simplifies config read in Scene constructor to help facilitate this.
2012-04-30 19:18:47 +01:00
Justin Clark-Casey (justincc) 6fa3dffad2 Use a fully stubbed out MockConsole for unit tests rather than inheriting from CommandConsole.
This is so that the static MainConsole.Instance doesn't retain references to methods registered by scene and other modules to service commands.
This prevents the scene from being garbage collected at the end of a test.
This is not the final thing preventing GC - next up is the timer started by SimStatsReporter that holds a reference to Scene that prevents end of test gc.
2012-04-30 19:17:06 +01:00
Justin Clark-Casey (justincc) 123e781cb3 Make TestSetPhysicsSinglePrim() actually add the object to the scene in order to test more code paths. 2012-04-30 19:15:07 +01:00
Justin Clark-Casey (justincc) ed9bf5b0c6 Add regression test for prim status when root prim in a new linkset is non-physical 2012-04-30 19:14:35 +01:00
Justin Clark-Casey (justincc) dd4e39ca1d refactor: extract common setup code in SceneObjectStatusTests 2012-04-30 19:13:33 +01:00
Justin Clark-Casey (justincc) 5a551b074b Add TestSetPhysics() to SOP status tests 2012-04-30 19:13:24 +01:00
Justin Clark-Casey (justincc) c1a9355865 Tweak log messages on local region to region teleport path to help with problem resolution. 2012-04-30 19:05:27 +01:00
Diva Canto 3ea6c25fb6 Teleports: bounce off repeated requests of teleporting the same agent. Some scripts do that, and that fails the whole thing. 2012-04-30 19:05:24 +01:00
Justin Clark-Casey (justincc) 2d3135448c Comment out old Scene.HandleLogOffUserFromGrid() to reduce client closing analysis complexity 2012-04-30 19:01:30 +01:00
Justin Clark-Casey (justincc) 36a99af37c minor: Add more detail to unauthorized caps client message 2012-04-30 19:01:23 +01:00
Justin Clark-Casey (justincc) 2aefd15913 minor: Add region name to dropped inbound packet message 2012-04-30 19:01:13 +01:00
Justin Clark-Casey (justincc) 6bc55b1086 minor: Add avatar name to removing agent log message 2012-04-30 19:00:47 +01:00
Justin Clark-Casey (justincc) 7058ce2c70 Comment out avatar move to target message for now. 2012-04-30 19:00:39 +01:00
Justin Clark-Casey (justincc) 8e111e9018 Add regression test TestSameRegionTeleport() 2012-04-30 18:59:50 +01:00
Justin Clark-Casey (justincc) d8bd7ca436 Comment out AvatarService.SetAvatar debug log line for now 2012-04-30 18:59:43 +01:00
Justin Clark-Casey (justincc) 392f73a000 Comment out some debug ATTACHMENTS log messages for now. 2012-04-30 18:59:35 +01:00
Justin Clark-Casey (justincc) 67efbaf33b Comment out the noisier AVFACTORY log messages for now.
Permanently comment out warnings about ScenePresence not being found - this is entirely expected if the avatar has alraedy logged out or left the scene.
2012-04-30 18:59:27 +01:00
Justin Clark-Casey (justincc) 0d06740148 Improve teleport log debug and error messages to tell us who is teleporting. 2012-04-30 18:58:37 +01:00
Diva Canto eda6947a22 Changed the Map-related messages from Info to Debug. They're debug messages. 2012-04-30 18:49:07 +01:00
Justin Clark-Casey (justincc) f331173145 Add online/offline indicator to "friends show" region console command.
Improve output table formatting.
2012-04-30 18:48:57 +01:00
Justin Clark-Casey (justincc) a666cd9e1f Add osForceAttachToAvatar() and osForceDetachFromAvatar()
These behave identically to llAttachToAvatar() and llDetachFromAvatar() except that they do not enforce the PERMISSION_ATTACH check
Intended for use in completely controlled dedicated environments where these checks are more a UI hinderance than a help.
Threat level high.
2012-04-24 00:50:32 +01:00
Justin Clark-Casey (justincc) 2657aa6987 Replace common code to fetch self inventory item (as opposed to uuid) with GetSelfInventoryItem()
However, at some point it would be far more convenient to receive the TaskInventoryItem in the constructor rather than just the item UUID, so we don't have to constantly refetch our self item.
2012-04-24 00:45:09 +01:00
Justin Clark-Casey (justincc) 6610cd2332 refactor: Replace calls to InventorySelf() with existing m_itemID in LSL_Api
There's no point look up an item ID that we already have.
2012-04-24 00:44:57 +01:00
Justin Clark-Casey (justincc) 0c8b44d514 Add more exception detail to Exception and IOException throws in BaseHttpServer.HandleRequest() 2012-04-24 00:40:19 +01:00
Justin Clark-Casey (justincc) ec46ff4445 Stop teleports from dropping tall avatars through or embedding them in the floor when lured by short avatars.
This involves giving the ceiling of the Z-component in a lure rather than the floor.
Ideally we would give the exact float compensating for relative avatar height but it looks like that isn't possible with the parcel id format used in lures
2012-04-24 00:37:37 +01:00
Melanie db891880e6 Fix a logic error in app domain creation 2012-04-24 00:36:54 +01:00
Melanie 9c433e8c50 Don't re-add the assembly resolver for each script if not creating the appdomain 2012-04-24 00:36:46 +01:00
Justin Clark-Casey (justincc) 5141863ae3 On "show part" command, show link number.
This replaces the Parts count which was rather pointless for a prim (it was either 1 if a child or the number of parts if the root).
This information is still avaliable on the "show object" command.
2012-04-18 23:02:02 +01:00
Justin Clark-Casey (justincc) d23550dea5 minor: Add some method doc. Add warnings since calling SOG link/delink methods directly rather than through Scene may allow race conditions. 2012-04-18 23:01:56 +01:00
Justin Clark-Casey (justincc) c3b12510ae Add TestGetChildPartPositionAfterObjectRotation() 2012-04-18 23:01:42 +01:00
Justin Clark-Casey (justincc) b2685e3671 Add test TestGetChildPartPosition() 2012-04-18 23:01:33 +01:00
Justin Clark-Casey (justincc) 601d2ddbf3 Move some public methods on WebStatsModule to private to reduce some static analysis complexity.
There's no obvious reason for these methods to be public.
2012-04-18 23:01:27 +01:00
Justin Clark-Casey (justincc) a5e8fe70af Use INSERT OR REPLACE INTO sql in WebStatsModule for session update rather than separate insert and update statements 2012-04-18 23:01:12 +01:00
Justin Clark-Casey (justincc) 9b3a96aa81 correct bug where f_invalid was being inserted on a webstats update for an existing session rather than d_world_kb 2012-04-18 23:01:06 +01:00
Justin Clark-Casey (justincc) d674e815bd Simplify WebStatsModule by removing the uncompleted migrations section.
Use "create table if not exists" instead.
Client stats data is transitory data that it is not worth migrating.
2012-04-18 23:01:00 +01:00
Justin Clark-Casey (justincc) c2e696d686 Fix bug in WebStatsModule where an exception would always be output on update if the user teleported to another region on that simulator.
This was because update was looking for an existing stats record unique in session id, agent id and region id.
But if the user teleports to another region then region id changes.
WebStatsModule promptly doesn't find the existing record and tries to insert a new one, but only session id is the primary key and that's still the same, which makes things go bang.
This makes the update search only on the unique session id.
This is only an issue with simulators that have multiple regions where the webstats module is enabled.
2012-04-18 23:00:51 +01:00
Justin Clark-Casey (justincc) 411dbb8df4 Add GroupPosition and GetWorldPosition() checks to TestGetRootPartPosition() 2012-04-18 23:00:43 +01:00
Justin Clark-Casey (justincc) b22dfbbf15 minor: make test names consistent 2012-04-18 23:00:36 +01:00
Justin Clark-Casey (justincc) 4c39a3fdbf refactor: move common init code into SetUp() in SceneObjectSpatialTests 2012-04-18 23:00:30 +01:00
Justin Clark-Casey (justincc) f3eb366f24 refactor: put SOG position test in a separate TestSceneObjectGroupPosition() 2012-04-18 23:00:22 +01:00
Justin Clark-Casey (justincc) a20d1b8f6b Add simple RelativePosition and OffsetPosition checks to TestGetRootPartPosition 2012-04-18 23:00:14 +01:00
Justin Clark-Casey (justincc) 0a51289332 Add very basic TestGetRootPartPosition() test 2012-04-18 23:00:02 +01:00
Justin Clark-Casey (justincc) 97ebfb00b4 Rather than having a FromFolderID property on every single prim and only ever using the root prim one, store on SOG instead.
This reduces pointless memory usage.
2012-04-18 22:59:24 +01:00
Justin Clark-Casey (justincc) c7d664f9d0 Store FromItemID for attachments once on SOG instead of on every SOP and only ever using the root part entry.
This eliminates some pointless memory use.
2012-04-18 22:59:18 +01:00
Justin Clark-Casey (justincc) c2fbaaa95d refactor: Eliminate unnecessary SOP.m_physActor 2012-04-18 22:59:08 +01:00
Justin Clark-Casey (justincc) 84d4b390f1 remove possible PhysActor unexpectedly null race conditions when changing prim collision status
factor out common SOP physics scene adding code into a common SOP.AddToPhysics() that is the counterpart to the existing RemoveFromPhysics()
2012-04-18 22:58:17 +01:00
Justin Clark-Casey (justincc) 54e59099b7 Fix more SOP.PhysActor race conditions in LSL_Api 2012-04-18 22:58:10 +01:00
Justin Clark-Casey (justincc) d9585ba37e Eliminate race condition where many callers would check SOP.PhysicsActor != null then assume it was still not null in later code.
Another thread could come and turn off physics for a part (null PhysicsActor) at any point.
Had to turn off localCopy on warp3D CoreModules section in prebuild.xml since on current nant this copies all DLLs in bin/ which can be a very large number with compiled DLLs
No obvious reason for doing that copy - nothing else does it.
2012-04-18 22:58:03 +01:00
Justin Clark-Casey (justincc) 45c617b5c3 Allow llRegionSayTo() to work on the PUBLIC_CHANNEL, as per http://wiki.secondlife.com/wiki/LlRegionSayTo
Addresses http://opensimulator.org/mantis/view.php?id=5950
2012-04-18 22:55:30 +01:00
Justin Clark-Casey (justincc) 4b0c78c64f minor: remove some now unneeded code from FriendsCommandsModule 2012-04-18 22:55:12 +01:00
Justin Clark-Casey (justincc) dd36e23a62 Make default "show friends" console command show friends fetched from the friends service.
There is no a --cache option which will show friends from the local cache if available.
2012-04-18 22:55:05 +01:00
Justin Clark-Casey (justincc) e2dade05d9 Lock NullFriendsData.m_Data for consistency and against concurrent read/write 2012-04-18 22:54:57 +01:00
Justin Clark-Casey (justincc) fbd61106cb refactor: Move "friends show cache" console command out into separate FriendsCommandsModule.
Expose required methods on IFriendsModule.  Rename GetFriends() -> GetFriendsFromCache() for self-documentation
2012-04-18 22:54:45 +01:00
Justin Clark-Casey (justincc) 67e66a2d34 Add simple login test with online friends. Add IFriendsModule.GrantRights() for granting rights via a module call.
Rename IFriendsModule.GetFriendPerms() -> GetRightsGrantedByFriend() to be more self-documenting and consistent with friends module terminology.
Add some method doc.
2012-04-18 22:46:10 +01:00
Justin Clark-Casey (justincc) 59911963ca refactor: Stop passing both IClientAPI and agentID to friend event listeners, these are redundant. Replace a few magic numbers with FriendRights enum already used elsewhere. 2012-04-18 22:45:31 +01:00
Justin Clark-Casey (justincc) dc2a4a6ccd Add simple regression test for logging in with offline friends. Don't expect to receive any in this instance. 2012-04-18 22:41:19 +01:00
Justin Clark-Casey (justincc) 81fb0b4f07 Add back parts of reverted changes that were not concerned with child agent caching.
This adds ScenePresence to IClientAPI.SceneAgent earlier on in the add client process so that its information is available to EventManager.OnNewClient() and OnClientLogin()
Also add a code comment as to why we're caching friend information for child agents.
2012-04-18 22:41:12 +01:00
Melanie a06c8fb7b2 Also add OSS header to interface 2012-04-18 22:41:05 +01:00
Melanie 05e70f76a9 Change namespace on CallingCardModule and correct interface file placemant. Also ass OpenSource header 2012-04-18 22:36:24 +01:00
Melanie b9f4836d3e Committing the Avination calling card module 2012-04-18 22:36:03 +01:00
Melanie ef77dc932b Adding the Avination calling card interface 2012-04-18 22:35:57 +01:00
Justin Clark-Casey (justincc) bd5b1d4d48 Earlier fix to remove localCopy on prebuild 2012-04-18 22:35:27 +01:00
Melanie 5c2ffa260c Pushing the Avination Calling card hooks. Module to follow. 2012-04-18 22:35:11 +01:00
Justin Clark-Casey (justincc) 23d04fa25c Add "friends show cache <first-name> <last-name>" command for debugging purposes.
This adds a reverse lookup (name -> ID) to IUserManagement instead of hitting the UserAccountService directly.
2012-04-18 22:11:42 +01:00
Justin Clark-Casey (justincc) 67dbce4512 minor: Add some documentation to OnNewClient and OnClientClosed events 2012-04-18 22:11:31 +01:00
Justin Clark-Casey (justincc) 46170fd0d8 minor: clean up some code formatting in VivoxVoiceModule.cs 2012-04-18 22:11:18 +01:00
Justin Clark-Casey (justincc) f85a453dc8 Allow the user to enter help topics in upper or lowercase.
Forcing uppercase (e.g. help Assets) is too annoying.
Thanks to WhiteStar for pointing this out.
2012-04-18 22:10:27 +01:00
nebadon bcfe48e05b fix yield prolog so it compiles with mono 2.11 there has been a bugzilla
report files with mono project in regards to this change, this simply
lets us move forward with using mono 2.11 for now :
https://bugzilla.xamarin.com/show_bug.cgi?id=4052
2012-04-18 22:10:05 +01:00
Justin Clark-Casey (justincc) 722ca250ea Move "change region" command into general category 2012-04-18 22:05:45 +01:00
Justin Clark-Casey (justincc) f4df128e52 Uses shorter AddCommand form for "show estates" 2012-04-18 22:05:38 +01:00
Justin Clark-Casey (justincc) 8cc5322b39 Display help commander topics in capitalized form - the commands themselves are still lowercase.
Also convert the estate commands to simply AddCommand() calls so that commands from two different modules can be placed in the same category
2012-04-18 22:05:24 +01:00
Justin Clark-Casey (justincc) 1480845597 Change "help" to display categories/module list then "help <category/module>" to display commands in a category.
This is to deal with the hundred lines of command splurge when one previously typed "help"
Modelled somewhat on the mysql console
One can still type help <command> to get per command help at any point.
Categories capitalized to avoid conflict with the all-lowercase commands (except for commander system, as of yet).
Does not affect command parsing or any other aspects of the console apart from the help system.
Backwards compatible with existing modules.
2012-04-18 22:05:03 +01:00
Justin Clark-Casey (justincc) c877e73463 Comment out log message about sending periodic appearance updates. 2012-03-30 02:33:26 +01:00
Justin Clark-Casey (justincc) 3ce5e8eb6c Add information about SendPeriodicAppearanceUpdates to OpenSimDefaults.ini for now.
Default remains false.
2012-03-30 02:33:18 +01:00
Justin Clark-Casey (justincc) e6b12e1f9d Add experimental SendPeriodicAppearanceUpdates = true/false setting to [Startup] in OpenSim.ini
On osgrid and other places, I have observed that manually sending appearance updates from the console often relieves grey avatar syndrome.
Despite hunting high and low, I haven't been able to find where this packet is sometimes being lost - it might be a persistent viewer bug for all I know.
Therefore, this experimental setting resends appearance data for everybody in the scene every 60 seconds.  These packets are small and the viewer only fetches texture
data if it doesn't already have it.
Default is false.
2012-03-30 02:33:10 +01:00
Justin Clark-Casey (justincc) bfbbd4ccba Add a scene maintenance thread in parallel to the heartbeat thread. The maintenance thread will end up running regular jobs that don't need to be in the main scene loop.
The idea is to make the critical main scene loop as skinny as possible - it doesn't need to run things that aren't time critical and don't depend on update ordering.
This will be done gradually over time to try and uncover any issues.  Many non-criticial scene loop activities are being launched on separate threadpool threads anyway.
This may also allow modules to register their own maintenance jobs without having to maintain their own timers and threads.
Currently the maintenance loop runs once a second, as opposed to the 89ms scene loop.
2012-03-30 02:33:01 +01:00
Justin Clark-Casey (justincc) fec7016665 Remove unnecessary shutting down check in Scene.Heartbeat(). Add some method doc. Rename HeartbeatThread, shuttingdown to conform to code standards. 2012-03-30 02:32:53 +01:00
Justin Clark-Casey (justincc) 47fe6170b2 Rename Scene.StartTimer() to Start() - this method no longer uses a timer. Comment out more effectively unused old heartbeat code. 2012-03-30 02:32:34 +01:00
Justin Clark-Casey (justincc) 24b5fb8523 Add commented out section on collisions switch in Scene.SetSceneCoreDebug().
This was not implemented before the recent changes but should be at some point.
2012-03-30 02:24:03 +01:00
Justin Clark-Casey (justincc) e8cd9688ce If "debug scene updates true" then print out to log when a garbage collection occurs. 2012-03-30 02:23:27 +01:00
Justin Clark-Casey (justincc) fa952f6d35 Add Scene.DebugUpdates switch which, if turned on, will print out a warning when a frame updates takes longer than twice the desired time
This is controlled via "debug scene updates true|false" on the region console.
Also fix an oversight with "debug scene teleport true|false"
2012-03-30 02:23:20 +01:00
Justin Clark-Casey (justincc) df55fd69af Incorporate scene teleporting debugging into "debug scene teleport true|false" command 2012-03-30 02:23:13 +01:00
Justin Clark-Casey (justincc) 019fc4c1f2 Replace "scene debug true false true" console command with "scene debug scripting true" or other parameters as appropriate.
This is to allow individual switching of scene debug settings and to provide flexibiltiy for additional settings.
2012-03-30 02:22:59 +01:00
Justin Clark-Casey (justincc) 22ea441feb fix compile error from last commit 2012-03-30 02:22:57 +01:00
Justin Clark-Casey (justincc) 9f5b33e52e refactor: simplify EstateManagementModule.handleEstateDebugRegionRequest() 2012-03-30 02:21:42 +01:00
Justin Clark-Casey (justincc) 92837c4f89 Add ability to log warn if a frame takes longer than twice the expected time. Currently commented out. 2012-03-30 02:21:32 +01:00
Justin Clark-Casey (justincc) 68ce06f40f remove unnecessary tmpFrameMS, use maintc instead for frame time calculation 2012-03-30 02:21:04 +01:00
Justin Clark-Casey (justincc) 279b31c75b Move frame loop entirely within Scene.Update() for better future performance analysis and stat accuracy.
Update() now accepts a frames parameter which can control the number of frames updated.
-1 will update until shutdown.
The watchdog updating moves above the maintc recalculation for any required sleep since it should be accounted for within the frame.
2012-03-30 02:20:46 +01:00
Justin Clark-Casey (justincc) 3117b3cd88 refactor: precalculate the fixed movement factor for avatar tilting (sqrt(2)) rather than doing it multiple times on every move. 2012-03-30 02:20:35 +01:00
Justin Clark-Casey (justincc) 9b547f76e7 refactor: Eliminate unnecessary duplicate avCapsuleTilted 2012-03-30 02:20:29 +01:00
Justin Clark-Casey (justincc) 3d6675784a Remove pointless ThreadAbortException catching in a test that isn't run anyway. 2012-03-30 02:20:21 +01:00
Justin Clark-Casey (justincc) 26f50eadd1 Remove some pointless catching/throwing in the scene loop. 2012-03-30 02:20:14 +01:00
Justin Clark-Casey (justincc) 6e8496ffc5 Comment out unused scene loop restart code.
This has actually been unused since at least 0.7.2 due to earlier changes.
2012-03-30 02:18:13 +01:00
Justin Clark-Casey (justincc) 88d6c4ec0e Use m_lastFrameTick instead of m_lastUpdate in Scene.GetHealth(). m_lastUpdate is no longer properly updated and is redundant anyway. 2012-03-30 02:18:05 +01:00
Justin Clark-Casey (justincc) 135eeb45d6 Change flavour to extended 2012-03-30 02:16:11 +01:00
Justin Clark-Casey (justincc) 54ee59c0bb Add Extended flavour option to opensim version information.
This flavour is for changes in addition to the 0.7.3-post-fixes branch that are too large to be considered fixes but should be reasonably stable.
This flavour will almost certainly never see a formal release.
2012-03-30 02:13:37 +01:00
Justin Clark-Casey (justincc) 4a5c61a33d Enable voice by default on parcels to weaken effects of viewer 2/3 ParcelVoiceInfoRequest bug
Viewer 2/3 contains a bug where the viewer will constantly retry ParcelVoiceInfoRequest requests on voice-disabled parcels where voice is otherwise available.
Attempts to fix this server-side have not been successful - sending a non-OK http code (e.g. a 404) just makes the viewer request again immediately.
Dropping the request entirely is a bit better but the viewer still retries after a minute.
Estate settings already enabled voice by default so doing the same for parcels.  This only has an effect if you have any voice system active at all.
Ultimately, the re-request bug needs to be fixed viewer-side (LL suffers from the same issue!) but it might be worth implementing the drop request hack.
2012-03-30 02:03:52 +01:00
Diva Canto 179c0f5f56 Merge branch '0.7.3-post-fixes' of ssh://opensimulator.org/var/git/opensim into 0.7.3-post-fixes 2012-03-22 20:36:01 -07:00
Justin Clark-Casey (justincc) 4dbf937707 Comment out login parameters debug output accidentally included with c4b2d24 2012-03-22 23:33:08 +00:00
Justin Clark-Casey (justincc) 9edb57e5e9 Comment out a terrain save-tile debugging message that accidentally crept in with c4b2d24 2012-03-22 23:17:54 +00:00
Justin Clark-Casey (justincc) 4d15ad63bf refactor: simplify code for checks when part.OwnerID != destPart.OwnerID in MoveTaskInventoryItem() 2012-03-22 23:03:00 +00:00
Justin Clark-Casey (justincc) 4021709371 Fix llGiveInventory() so that it checks the destination part for AllowInventoryDrop, not the source.
This allows llAllowInventoryDrop() to work.
Regression test added for this case.
2012-03-22 23:02:49 +00:00
Justin Clark-Casey (justincc) 3c13f5c3aa Add llGiveInventory() test from object to object where both objects are owned by the same user. 2012-03-22 23:02:35 +00:00
Justin Clark-Casey (justincc) 381517b451 Add prim name to "[MESH]: No recognized physics mesh..." log message 2012-03-22 23:01:39 +00:00
Justin Clark-Casey (justincc) 6ecf36d49c Fix small typo 2012-03-22 22:47:03 +00:00
Justin Clark-Casey (justincc) 64eb4b8408 Fix crash where two scene loop threads could changes m_MeshToTriMeshMap at the same time.
Have to lock m_MeshToTriMeshMap as property is static and with more than one region two scene loops could try to manipulate at the same time.
2012-03-22 22:46:45 +00:00
Diva Canto 318da3fdcd Added new simple_build_permissions config to the .ini and .example files. 2012-03-22 14:34:46 -07:00
Melanie 50c99fcda6 Change a false false to be truly true - or is this statement false?
Fixes perms boo-boo
2012-03-22 14:34:33 -07:00
Melanie 321de1f263 Rework Diva's patch to simplify it 2012-03-22 14:34:19 -07:00
Diva Canto fa30ace67d Fixed borkness with map search introduce by my last changes to it. 2012-03-20 17:49:05 -07:00
Justin Clark-Casey (justincc) 92baa79253 Add some doc about the EventManager.OnLoginsEnabled event. 2012-03-19 22:49:34 +00:00
Justin Clark-Casey (justincc) e861b45313 Fix a bug where logins to standalones would fail if the RegionReady module was not active
Unfortunately, the OnLoginsEnabled event is currently only guaranteed to fire if the RegionReady module is active.
However, we can instantiate the AuthorizationService in the module RegionLoaded method since by this time all other modules will have been loaded
2012-03-19 22:49:14 +00:00
Justin Clark-Casey (justincc) cf91ac68b6 Stop console command "xengine status" throwing an exception if there are no scripts in a region.
Addresses http://opensimulator.org/mantis/view.php?id=5940
2012-03-19 21:45:18 +00:00
Justin Clark-Casey (justincc) d4aba13526 Clean up "save iar" help 2012-03-19 21:33:03 +00:00
Justin Clark-Casey (justincc) d36c7c3782 minor: reuse threadpool count we just fetched instead of fetching it again 2012-03-19 21:32:50 +00:00
Justin Clark-Casey (justincc) 4385fcdeae Add total scripts count to "show threads"
However, this returns 0 on Mono (at least on 2.6.7)!  So not showing if it is zero.
2012-03-19 21:32:42 +00:00
Justin Clark-Casey (justincc) 04eb170624 refactor: separate out console and status report generation parts of XEngine 2012-03-19 21:32:34 +00:00
Justin Clark-Casey (justincc) 4803686078 Improve threadpool reporting to "show threads" console command (also gets printed out periodically) 2012-03-19 21:32:25 +00:00
Justin Clark-Casey (justincc) 20b0fda3bb Add process working memory to "show stats" memory statistics.
This shows the actual amount of RAM being taken up by OpenSimulator (objects + vm overhead)
2012-03-19 21:32:16 +00:00
Justin Clark-Casey (justincc) 4e5f823595 In Top Scripts report, don't show scripts with no or less than 1 microsecond of execution time.
This is to make the report clearer and less confusing.
2012-03-19 21:32:02 +00:00
Justin Clark-Casey (justincc) a74408d1d2 Aggregate script execution times by linksets rather than individual prims.
This is for the top scripts report.
2012-03-19 21:31:53 +00:00
Justin Clark-Casey (justincc) 8206537efd Fix owner name display in "Top Colliders" and "Top Script" region reports. 2012-03-19 21:31:45 +00:00
Justin Clark-Casey (justincc) a9a77bb3ab Replace script-lines-per-second with the script execution time scaled by its measurement period and an idealised frame time.
The previous lines-per-second measurement used for top scripts report was inaccurate, since lines executed does not reflect time taken to execute.
Also, every fetch of the report would reset all the numbers limiting its usefulness and we weren't even guaranteed to see the top 100.
The actual measurement value should be script execution time per frame but XEngine does not work this way.
Therefore, we use actual script execution time scaled by the measurement period and an idealised frame time.
This is still not ideal but gives reasonable results and allows scripts to be compared.
This commit moves script execution time calculations from SceneGraph into IScriptModule implementations.
2012-03-19 21:31:38 +00:00
Justin Clark-Casey (justincc) 6390de689d Remove property/field duplication in ScriptInstance where it's unnecessary. 2012-03-19 21:31:17 +00:00
Justin Clark-Casey (justincc) 41ce19836b Simplify some logic in the ScriptInstance constructor - running is set to false in both if/else branches 2012-03-19 21:31:07 +00:00
Melanie 883a4f6fff FireAndForget scripted rez - port from Avination 2012-03-19 21:30:31 +00:00
Justin Clark-Casey (justincc) 5f1da80fc1 minor: correct indentation levels 2012-03-19 21:30:16 +00:00
Justin Clark-Casey (justincc) 64217d67f6 Remove duplication of m_RunEvents and Running 2012-03-19 21:29:45 +00:00
Justin Clark-Casey (justincc) b01c79354c Fix a problem where multiple near simultaneous calls to llDie() from multiple scripts in the same linkset can cause unnecessary thread aborts.
The first llDie() could lock Scene.m_deleting_scene_object.
The second llDie() would then wait at this lock.
The first llDie() would go on to remove the second script but always abort it since the second script's WorkItem would not go away.
Easiest solution here is to remove the m_deleting_scene_object since it's no longer justified - we no longer lock m_parts but take a copy instead.
This also requires an adjustment in XEngine.OnRemoveScript not to use instance.ObjectID instead when firing the OnObjectRemoved event.
2012-03-19 21:29:34 +00:00
Justin Clark-Casey (justincc) 9ecbcb787c Alleviate an issue where calling Thread.Abort() on script WorkItems can fail to release locks, resulting in a crippled simulator.
This seems to be a particular problem with ReaderWriterLockSlim, though other locks can be affected as well.
It has been seen to happen when llDie() is called in a linkset running more than one script.
Alleviation here means supplying a ScriptInstance.Stop() timeout of 1000ms rather than 0ms, to give events a chance to complete.
Also, we check the IsRunning status at the top of the ScriptInstance.EventProcessor() so that another event doesn't start in the mean time.
Ultimately, a better solution may have to be found since a long-running event would still exceed the timeout and be aborted.
2012-03-19 21:29:24 +00:00
Justin Clark-Casey (justincc) e17e376b04 refactor: rename ScriptInstance.m_CurrentResult to m_CurrentWorkItem to make it more understandable as to what it is and what it does (hold a thread pool work item for a waiting of in-progress event)
Also add other various illustrative comments
2012-03-19 21:29:09 +00:00
Justin Clark-Casey (justincc) 1b4ea4f178 Add max thread and min thread information to "xengine status" region console command 2012-03-19 21:28:54 +00:00
Melanie 1de29fb362 Change OpenSim.ini.example to reflect how to actually enable prim limits,
as opposed to how it was first designed.
2012-03-19 21:28:37 +00:00
Justin Clark-Casey (justincc) 7e4bd492fd Add documentation to make more explicit the difference between OnRezScript and OnNewScript in the event manager
OnNewScript fires when a script is added to a scene
OnRezScript fires when the script actually runs (i.e. after permission checks, state retrieval, etc.)
2012-03-19 21:26:32 +00:00
Justin Clark-Casey (justincc) 588d56503d Remove unnecessary explicit ElapsedEventHandler in SimReporter 2012-03-19 21:26:12 +00:00
Justin Clark-Casey (justincc) e9602656f8 Add sensor, dataserver requests, timer and listener counts to "xengine status" command.
This is for diagnostic purposes.
2012-03-19 21:26:04 +00:00
Justin Clark-Casey (justincc) 0116d418f0 Fix TestSyntaxError() and TestSyntaxErrorDeclaringVariableInForLoop()
They were all failing assertions but the exceptions these threw were caught as expected Exceptions.
I don't think we can easily distinguish these from the Exceptions that we're expecting.
So for now we'll do some messy manually checking with boolean setting instead.
This patch also corrects the assertions themselves.
2012-03-19 21:25:41 +00:00
Justin Clark-Casey (justincc) 9992974c66 Get all test methods in OpenSim.Region.ScriptEngine.Tests.dll to report that they're running 2012-03-19 21:25:30 +00:00
Justin Clark-Casey (justincc) ba27d8a389 Fix off by one error in script error reporting. 2012-03-19 21:25:20 +00:00
Justin Clark-Casey (justincc) f96e985763 Simplify NPCModuleTests code by putting the NPCModule in an instance variable rather than making each test fetch it seperately.
Also rename instance variables in the test to conform to naming standards and for understandability
2012-03-19 21:25:07 +00:00
Diva Canto 5b9eaae50d Region access control! Region operators can now specify things like DisallowForeigners (means what it says) and DisallowResidents (means that only admins and managers can get into the region). This puts the never-completed AuthorizationService to good use. Note that I didn't implement a grid-wide Authorization service; this service implementation is done entirely locally on the simulator. This can be changed as usual by pluging in a different AuthorizationServicesConnector. 2012-03-17 19:49:14 -07:00
Diva Canto 74a13f7e3b Fixes mantis #5923 2012-03-17 19:47:05 -07:00
Diva Canto 3e88fc8aad Datasnapshot: added "secret" to the registration/deregistration query so that data providers can verify authenticity if they want. 2012-03-16 17:27:45 -07:00
Diva Canto 881740d702 DataSnapshot: renamed gridserverURL to gatekeeperURL, and normalimzed the capitalization of 'name' to lower case, also in the same <grid> section. 2012-03-16 17:27:29 -07:00
Diva Canto 9a643a1bb9 Terrain: added [Terrain] section with an option to load an initial flat terrain. Default is still pinhead island. I much rather have a flat land in the beginning.
Conflicts:

	bin/OpenSim.ini.example
2012-03-16 13:14:26 -07:00
Diva Canto a5488650ff More on map search: send extra messages to the user regarding the region being found or not, because the UI is horribly confusing -- places profile is always "loading..." whether the region exists or not. 2012-03-15 20:24:26 -07:00
Diva Canto a275127a65 More on SLURLs and V3. This is hacky, but it works. Basically, we have to redefine the encoding of HG URLs because the viewer messes them up. Examples of what works and doesn't work:
- secondlife://ucigrid00.nacs.uci.edu|8002/128/128 <-- works throughout the viewer
- secondlife://http|!!ucigrid00.nacs.uci.edu|8002+Test+Zone+1/128/128 <-- works throughout the viewer
- secondlife://http|!!grid.sciencesim.com!grid!hypergrid.php+Yellowstone01+74/128/128 <-- works throughout
- secondlife://http%3A%2F%2Fucigrid00.nacs.uci.edu%3A8002%20UCI%20Central%201/128/128 <-- works in chat, but not as URLs in the webkit
2012-03-15 16:14:20 -07:00
Diva Canto 02db31db6a Revert "Revert "More hacking around viewer bug""
This reverts commit 0434758a0d.
2012-03-15 16:14:07 -07:00
Diva Canto 8bd813e6fc Revert "Revert "Hack around https://jira.secondlife.com/browse/VWR-28570""
This reverts commit 09ff121654.
2012-03-15 16:13:50 -07:00
Diva Canto 09ff121654 Revert "Hack around https://jira.secondlife.com/browse/VWR-28570"
This reverts commit d7651a389e.
2012-03-15 14:38:22 -07:00
Diva Canto 0434758a0d Revert "More hacking around viewer bug"
This reverts commit 8bb0a71083.
2012-03-15 14:38:03 -07:00
Diva Canto 8bb0a71083 More hacking around viewer bug 2012-03-15 11:04:56 -07:00
Diva Canto d7651a389e Hack around https://jira.secondlife.com/browse/VWR-28570 2012-03-15 10:18:55 -07:00
Justin Clark-Casey (justincc) 824318a0c1 Go back to setting appearance directly in NPCModule.SetAppearance() to fix mantis 5914
The part reverted is from commit 2ebb421.
Unfortunately, IAvatarFactoryModule.SetAppearance() does not transfer attachments.
I'm not sure how to do this separately, unfortunately I'll need to leave it to Dan :)
Regression test added for this case.
Mantis ref: http://opensimulator.org/mantis/view.php?id=5914
2012-03-06 01:32:44 +00:00
Justin Clark-Casey (justincc) 5e9ed22e84 Merge branch '0.7.3-post-fixes' of ssh://opensimulator.org/var/git/opensim into 0.7.3-post-fixes 2012-03-06 00:48:24 +00:00
Chris Hart a6c611e7c9 Updates to MSSQL store for 0.7.3 to include:
* Telehub support
* Bugfix to Friends lookups
* Updates to Creator fields to store up to 255 characters for HG item creator storage
2012-03-06 00:45:58 +00:00
Diva Canto 72b325f8b5 Send the right name and creation date on the BasicProfileModule. 2012-03-01 19:58:57 -08:00
Justin Clark-Casey (justincc) 54d0514b13 Move SenseRepeaters.Count check inside the SenseRepeatListLock.
No methods in the List class are thread safe in the MS specification/documentation
2012-03-02 00:37:48 +00:00
Justin Clark-Casey (justincc) 58b1c3cec0 lock SenseRepeatListLock when added a new sensor during script reconstitution.
This is already being done in the other place where a sensor is added.
Adding a sensor whilst another thread is iterating over the sensor list can cause a concurrency exception.
2012-03-02 00:37:42 +00:00
Justin Clark-Casey (justincc) 71641523a3 Flick 0.7.3-post-fixes to Flavour.Post_Fixes 2012-02-29 23:43:41 +00:00
Justin Clark-Casey (justincc) 94c5e25c3b Extend distsrc target to cleanup the files generated by running prebuild 2012-02-29 22:32:32 +00:00
Justin Clark-Casey (justincc) 20bad0aa6c Merge branch '0.7.3-post-fixes' of ssh://opensimulator.org/var/git/opensim into 0.7.3-post-fixes 2012-02-29 22:22:42 +00:00
Justin Clark-Casey (justincc) e7f23a6218 Switch flavour to release 2012-02-29 22:10:33 +00:00
Justin Clark-Casey (justincc) 25c29db8b6 Don't start pCampbot if the user doesn't supply bot firstname, lastname stub and password 2012-02-29 22:04:17 +00:00
Justin Clark-Casey (justincc) 1750fba9ce Call Dispose() via using() on SqliteCommands in WebStatsModule after use. 2012-02-29 22:04:04 +00:00
PixelTomsen b18e410586 PRIM_SCULPT_FLAG_INVERT, PRIM_SCULPT_FLAG_MIRROR implemented
http://opensimulator.org/mantis/view.php?id=5763
2012-02-29 22:03:28 +00:00
Justin Clark-Casey (justincc) 38d5e1fab3 Move libopenjpeg native libraries into lib32 and lib64 as appropriate. 2012-02-29 22:03:15 +00:00
Justin Clark-Casey (justincc) 4180c32eb1 Remove some more unused Bullet libraries. 2012-02-29 22:03:08 +00:00
Justin Clark-Casey (justincc) 5115229fdf Remove old libbulletnet native libraries. These are not used in the current generation bullet physics plugin. 2012-02-29 22:02:59 +00:00
Justin Clark-Casey (justincc) e8f2d814e7 Move other sqlite and ode 32-bit and 64-bit libraries into lib32 or lib64 as appropriate. 2012-02-29 22:02:50 +00:00
Diva Canto 1fda8c5a86 HG: Remove async in posting assets to foreign grid. Mono hates concurrency there. 2012-02-26 14:30:58 -08:00
Justin Clark-Casey (justincc) 6b77b55d40 Merge branch 'master' into 0.7.3-post-fixes 2012-02-25 00:49:16 +00:00
Justin Clark-Casey (justincc) 82cdb08c1f Merge branch 'master' into 0.7.3-post-fixes 2012-02-25 00:49:05 +00:00
Justin Clark-Casey (justincc) dafcb3bcd7 Merge branch 'master' into 0.7.3-post-fixes 2012-02-24 23:35:59 +00:00
Diva Canto 3259b1d1e0 Amend to last commit: synchronize access to queues. 2012-02-20 11:13:02 -08:00
Diva Canto 512910a51f More improvements on agent position updates: if the target sims fail, blacklist them for 2 min, so that we don't keep doing remote calls that fail. 2012-02-20 11:00:01 -08:00
Diva Canto fdda57cf10 Merge branch '0.7.3-post-fixes' of ssh://opensimulator.org/var/git/opensim into 0.7.3-post-fixes 2012-02-19 16:48:09 -08:00
Diva Canto ec8e34950d One more tweak related to the previous 2 commits. 2012-02-19 16:47:04 -08:00
Diva Canto 93964ef3a4 Amend to last commit. This should have been committed too. 2012-02-19 16:46:47 -08:00
Diva Canto 5c8af6a136 A few more tweaks on position updates and create child agents. Mono hates concurrent uses of the same TCP connection, and even of the connections to the same server. So let's stop doing it. This patch makes movement much smoother when there are lots of neighbours. 2012-02-19 16:46:32 -08:00
PixelTomsen 4d0c8aca05 Fix:OmegaX, OmegaY and OmegaZ not saved for child prims http://opensimulator.org/mantis/view.php?id=5893
Signed-off-by: nebadon <michael@osgrid.org>
2012-02-19 13:49:51 -05:00
PixelTomsen 8fc16ece96 Fix:Fly setting for Parcel dosen't work http://opensimulator.org/mantis/view.php?id=5887
Signed-off-by: nebadon <michael@osgrid.org>
2012-02-19 13:49:41 -05:00
BlueWall fcbb375e8f Use localy defined name, TPFlags, for Constants.TeleportFlags 2012-02-19 12:42:05 -05:00
BlueWall 49c65279fa Route logins according to Estate, Telehub and TeleportFlags 2012-02-19 12:41:44 -05:00
BlueWall 86e8a56fe1 Propagate our teleport flags on logins 2012-02-19 12:41:30 -05:00
BlueWall b199330682 Parcel sales support to SQLite 2012-02-19 12:41:11 -05:00
BlueWall 7a7ebaebd1 Fillin missing SQLite support for Telehubs 2012-02-19 12:40:46 -05:00
BlueWall 164ae0b24b Fix missing telehub handling on login 2012-02-19 12:40:26 -05:00
Diva Canto 7156545fca This should smooth movement in heteregeneous networks by a lot: cache the region by position instead of looking it up all the time -- this was being done during the main update loop! 2012-02-18 22:15:58 -08:00
Justin Clark-Casey (justincc) 73a5abf4d9 Merge branch 'master' into 0.7.3-post-fixes 2012-02-17 04:04:38 +00:00
Justin Clark-Casey (justincc) 630c8dc828 switch version flavour to rc2 2012-02-17 04:03:59 +00:00
Justin Clark-Casey (justincc) 6de89246c2 Merge branch 'master' into 0.7.3-post-fixes 2012-02-17 00:02:16 +00:00
Justin Clark-Casey (justincc) 96973a5778 Merge branch 'master' into 0.7.3-post-fixes 2012-02-16 03:39:25 +00:00
Justin Clark-Casey (justincc) 96843f2b17 Merge branch 'master' into 0.7.3-post-fixes 2012-02-15 02:41:50 +00:00
Justin Clark-Casey (justincc) 8a36f54cf4 Merge branch 'master' into 0.7.3-post-fixes 2012-02-06 20:54:21 +00:00
Justin Clark-Casey (justincc) 1a14e660d2 Merge branch 'master' into 0.7.3-post-fixes 2012-02-04 02:03:49 +00:00
Justin Clark-Casey (justincc) 2502aae5db Switch flavour to RC1. It will still be a while before RC1 is released. 2012-02-04 01:26:29 +00:00
1981 changed files with 207102 additions and 396339 deletions

29
.gitignore vendored
View File

@ -1,6 +1,5 @@
.project
.settings
.gitignore
*.csproj
*.csproj.user
*.build
@ -11,13 +10,6 @@
*.pidb
*.dll.build
*.dll
*.log
# Ignore .user and .suo files as these are user preference specific
# http://stackoverflow.com/questions/72298/should-i-add-the-visual-studio-suo-and-user-files-to-source-control
*.suo
*.user
*.VisualState.xml
*/*/obj
*/*/*/obj
@ -31,19 +23,12 @@
*/*/*/*/*/bin
*/*/*/*/*/*/bin
*/*/*/*/*/*/*/bin
.vs/
addon-modules/
bin/Debug/*.dll
bin/*.dll.mdb
bin/*.db
bin/*.db-journal
bin/addin-db-*
bin/*.dll
bin/OpenSim.vshost.exe.config
bin/OpenSim.32BitLaunch.vshost.exe.config
bin/OpenSim.32BitLaunch.log
UpgradeLog.XML
_UpgradeReport_Files/
bin/ScriptEngines/*-*-*-*-*
bin/ScriptEngines/*.dll
bin/ScriptEngines/*/*.dll
@ -58,7 +43,6 @@ bin/Regions/*
bin/UserAssets
bin/assetcache
bin/maptiles
bin/bakes
bin/estate_settings.xml
bin/config-include/CenomeCache.ini
bin/config-include/FlotsamCache.ini
@ -71,18 +55,12 @@ bin/OpenSim.Grid.InventoryServer.log
bin/OpenSim.Grid.MessagingServer.log
bin/OpenSim.Grid.UserServer.log
bin/OpenSim.log
bin/OpenSimStats.log
bin/Robust.log
bin/RobustStats.log
bin/OpenSimConsoleHistory.txt
bin/RobustConsoleHistory.txt
bin/*.Tests.log
bin/*.manifest
bin/crashes/
Examples/*.dll
OpenSim.build
OpenSim.sln
OpenSim.userprefs
OpenSim.suo
Prebuild/Prebuild.build
Prebuild/Prebuild.sln
TestResult.xml
@ -94,6 +72,7 @@ TAGS
Makefile.local
bin/.version
compile.bat
addon-modules
OpenSim/Data/Tests/test-results/
OpenSim/Framework/Serialization/Tests/test-results/
OpenSim/Framework/Servers/Tests/test-results/
@ -110,7 +89,3 @@ OpenSim/Region/ScriptEngine/test-results/
OpenSim/Tests/Common/test-results/
OpenSim/Tests/test-results/
test-results/
doc/html
doc/doxygen.error.log
*.patch

View File

@ -43,11 +43,10 @@
<delete dir="${distbindir}/Prebuild"/>
<delete dir="${distbindir}/%temp%"/>
<delete dir="${distbindir}/.nant"/>
<delete dir="${distbindir}/ThirdParty"/>
<delete>
<fileset basedir="${distbindir}">
<include name="compile.bat"/>
<include name="BUILDING.md"/>
<include name="BUILDING.txt"/>
<include name="Makefile"/>
<include name="nant-color"/>
<include name="OpenSim.*"/>
@ -113,12 +112,10 @@
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.coremodules.tests)==0}" />
<!--
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.region.optionalmodules.tests">
<arg value="./bin/OpenSim.Region.OptionalModules.Tests.dll" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.optionalmodules.tests)==0}" />
-->
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.region.framework.tests">
<arg value="./bin/OpenSim.Region.Framework.Tests.dll" />
@ -135,22 +132,7 @@
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.server.handlers.tests">
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.server.handlers.tests)==0}" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.services.inventoryservice.tests">
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.services.inventoryservice.tests)==0}" />
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.tests.permissions">
<arg value="./bin/OpenSim.Tests.Permissions.dll" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.tests.permissions)==0}" />
<delete dir="%temp%"/>
<delete dir="%temp%"/>
</target>
<target name="test-stress" depends="build, find-nunit">
@ -233,12 +215,10 @@
<arg value="-xml=test-results/OpenSim.Region.CoreModules.Tests.dll-Results.xml" />
</exec>
<!--
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.region.optionalmodules.tests">
<arg value="./bin/OpenSim.Region.OptionalModules.Tests.dll" />
<arg value="-xml=test-results/OpenSim.Region.OptionalModules.Tests.dll-Results.xml" />
</exec>
-->
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.region.framework.tests">
<arg value="./bin/OpenSim.Region.Framework.Tests.dll" />
@ -255,33 +235,16 @@
<arg value="-xml=test-results/OpenSim.Capabilities.Handlers.Tests.dll-Results.xml" />
</exec>
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.server.handlers.tests">
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
<arg value="-xml=test-results/OpenSim.Server.Handlers.Tests.dll-Results.xml" />
</exec>
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.services.inventoryservice.tests">
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
<arg value="-xml=test-results/OpenSim.Services.InventoryService.Tests.dll-Results.xml" />
</exec>
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.tests.permissions">
<arg value="./bin/OpenSim.Tests.Permissions.dll" />
<arg value="-xml=test-results/OpenSim.Tests.Permissions.dll-Results.xml" />
</exec>
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.framework.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.framework.servers.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.clientstack.lindenudp.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.scriptengine.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.coremodules.tests)==0}" />
<!-- <fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.optionalmodules.tests)==0}" /> -->
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.optionalmodules.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.framework.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.data.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.services.inventoryservice.tests)==0}" />
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.tests.permissions)==0}" />
</target>
<target name="doxygen">

View File

@ -1,37 +0,0 @@
# Building on Windows
Steps:
* runprebuild.bat
* Load OpenSim.sln into Visual Studio .NET and build the solution.
* chdir bin
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* run OpenSim.exe
# Building on Linux / Mac
Prereqs:
* Mono > 5.0
* On some Linux distributions you may need to install additional packages.
* msbuild or xbuild if still supported by the mono version
* See http://opensimulator.org/wiki/Dependencies for more information.
From the distribution type:
* ./runprebuild.sh
* type msbuild or xbuild
* cd bin
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* review and change those ini files according to your needs
* windows: execute opensim.exe or opensim32.exe for small regions
* linux: run ./opensim.sh
* msbuild (xbuild) option switches
* clean: msbuild /target:clean
* debug: (default) msbuild /property:Configuration=Debug
* release: msbuild /property:Configuration=Release
# References
Helpful resources:
* http://opensimulator.org/wiki/Build_Instructions

39
BUILDING.txt Normal file
View File

@ -0,0 +1,39 @@
==== Building OpenSim ====
=== Building on Windows ===
Steps:
* runprebuild.bat
* Load OpenSim.sln into Visual Studio .NET and build the solution.
* chdir bin
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* run OpenSim.exe
=== Building on Linux ===
Prereqs:
* Mono >= 2.4.3
* Nant >= 0.85
* On some Linux distributions you may need to install additional packages.
See http://opensimulator.org/wiki/Dependencies for more information.
* May also use xbuild (included in mono distributions)
* May use Monodevelop, a cross-platform IDE
From the distribution type:
* ./runprebuild.sh
* nant (or xbuild)
* cd bin
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* run mono OpenSim.exe
=== Using Monodevelop ===
From the distribution type:
* ./runprebuild.sh
* type monodevelop OpenSim.sln
=== References ===
Helpful resources:
* http://opensimulator.org/wiki/Build_Instructions

View File

@ -1,22 +1,39 @@
The following people have contributed to OpenSim (Thank you for your effort!)
<<<>>>>The following people have contributed to OpenSim (Thank you
for your effort!)
= Current OpenSim Developers (in very rough order of appearance) =
These folks represent the current core team for OpenSim, and are the
people that make the day to day of OpenSim happen.
* justincc (OSVW Consulting, justincc.org)
* chi11ken (Genkii)
* dahlia
* Melanie Thielker
* Diva (Crista Lopes, University of California, Irvine)
* Robert Adams (MisterBlue)
* Kevin Cozens
* Leal Duarte (Ubit Umarov)
* Dan Lake (Intel)
* Marck
* Mic Bowman (Intel)
* BlueWall (James Hughes)
* Nebadon Izumi (Michael Cerquoni, OSgrid)
* Snoopy Pfeffer
* Richard Adams (Intel)
= Core Developers Following the White Rabbit =
Core developers who have temporarily (we hope) gone chasing the white rabbit.
They are in all similar to the active core developers, except that they haven't
been that active lately, so their voting rights are awaiting their come back.
* Nebadon Izumi (Michael Cerquoni, OSgrid)
* Alicia Raven
* MW (Tribal Media AB)
* Adam Frisby (DeepThink Pty Ltd)
* lbsa71 (Tribal Media AB)
* Teravus (w3z)
* Ckrinke (Charles Krinke)
* Dr Scofield aka Dirk Husemann (IBM Research - Zurich)
* mikem (3Di)
* Homer_Horwitz
* nlin (3Di)
* Arthur Rodrigo S Valadares (IBM)
* John Hurliman
= Past Open Sim Developers =
These folks are alumns of the OpenSim core group, but are now
@ -38,123 +55,65 @@ where we are today.
* adjohn (Genkii)
* idb (Ian Brown)
* Johan Berntsson (3Di)
* MW (Tribal Media AB)
* Adam Frisby (DeepThink Pty Ltd)
* lbsa71 (Tribal Media AB)
* Ckrinke (Charles Krinke)
* Dr Scofield aka Dirk Husemann (IBM Research - Zurich)
* mikem (3Di)
* Homer_Horwitz
* nlin (3Di)
* John Hurliman
* chi11ken (Genkii)
* dahlia
* justincc (OSVW Consulting, justincc.org)
* Arthur Rodrigo S Valadares (IBM)
* BlueWall (James Hughes)
* Dan Lake
* Marck
* Mic Bowman
* Oren Hurvitz (Kitely)
* Snoopy Pfeffer
* Teravus (w3z)
= Additional OpenSim Contributors =
These folks have contributed code patches or content to OpenSimulator to help make it
These folks have contributed code patches to OpenSim to help make it
what it is today.
* A_Biondi
* aduffy70
* Ai Austin
* A_Biondi
* alex_carnell
* Alan Webb (IBM)
* Aleric
* Allen Kerensky
* BigFootAg
* Bill Blight
* BlueWall Slade
* bobshaffer2
* brianw/Sir_Ahzz
* CharlieO
* ChrisDown
* Chris Yeoh (IBM)
* cinderblocks
* controlbreak
* coyled
* ctrlaltdavid (David Rowe)
* Daedius
* daTwitch
* Dev Random
* devalnor-#708
* dmiles (Daxtron Labs)
* Dong Jun Lan (IBM)
* DoranZemlja
* Drake Arconis
* dr0b3rts
* dslake
* eeyore
* daTwitch
* devalnor-#708
* dmiles (Daxtron Labs)
* dslake (Intel)
* FredoChaplin
* FreakyTech
* Garmin Kawaguichi
* Gavin Hird
* Gerhard
* Godfrey
* Greg C.
* Grumly57
* GuduleLapointe
* Ewe Loon
* Fernando Oliveira
* Fly-Man
* Flyte Xevious
* Freaky Tech
* Garmin Kawaguichi
* Geir Noklebye
* Glenn Martin (MOSES)
* Gryc Ueusp
* H-H-H (ginge264)
* Hiro Lecker
* Iain Oliver
* Imaze Rhiano
* Intimidated
* Jak Daniels
* Jeff Kelly
* Jeremy Bongio (IBM)
* jhurliman
* John R Sohn (XenReborn)
* jonc
* Jon Cundill
* Junta Kohime
* Kayne
* Kevin Cozens
* kinoc (Daxtron Labs)
* Kira
* Kitto Flora
* KittyLiu
* Kurt Taylor (IBM)
* Lani Global
* lickx
* lillith_xue
* lkalif
* LuciusSirnah
* lulurun
* M.Igarashi
* Magnuz Binder
* maimedleech
* Mana Janus
* Mandarinka Tasty
* MarcelEdward
* Matt Lehmann
* mewtwo0641
* Mic Bowman
* Michelle Argus
* Michael Cortez (The Flotsam Project, http://osflotsam.org/)
* Michael Heilmann (MOSES)
* Micheil Merlin
* Mike Osias (IBM)
* Mike Pitman (IBM)
* Mike Rieker (Dreamnation)
* mikemig
* mikkopa/_someone - RealXtend
* Misterblue
* Misterblue (Intel)
* Mircea Kitsune
* mpallari
* MrMonkE
@ -163,75 +122,64 @@ what it is today.
* nornalbion
* Omar Vera Ustariz (IBM)
* openlifegrid.com
* Oren Hurvitz (Kitely)
* otakup0pe
* Pixel Tomsen
* Quill Littlefeather
* ralphos
* RemedyTomm
* Revolution
* Richard Alimi (IBM)
* Rick Alther (IBM)
* Rob Smart (IBM)
* Robert Louden (MOSES)
* Roger Kirkman (zadark)
* rtomita
* Ruud Lathorp
* SachaMagne
* Salahzar Stenvaag
* satguru p srivastava
* sempuki
* Shy Robbiani
* SignpostMarv
* SpotOn3D
* Stefan_Boom / stoehr
* Steven Zielinski (MOSES)
* Stolen Ruby
* Strawberry Fride
* Talun
* TechplexEngineer (Blake Bourque)
* TBG Renfold
* Terry Ford
* tglion
* tlaukkan/Tommil (Tommi S. E. Laukkanen, Bubble Cloud)
* TomDataWorks
* TomTheDragon (muckwaddle)
* tyre
* uriesk
* Vegaslon <vegaslon@gmail.com>
* Vincent Sylvester
* VikingErik
* Vytek
* webmage (IBM)
* Xantor
* Y. Nitta
* YoshikoFazuku
* YZh
* Zackary Geers aka Kunnis Basiat
* Zha Ewry
* ziah
= LSL Devs =
* Alondria
* CharlieO
* Tedd
* Melanie Thielker
= Testers =
* Ai Austin
* CharlieO (LSL)
* Ckrinke
* openlifegrid.com
This software uses components from the following developers:
* Sleepycat Software (Berkeley DB)
* Aurora-Sim (http://aurora-sim.org)
* SQLite (Public Domain)
* XmlRpcCS (http://xmlrpccs.sf.net/)
* MySQL, Inc. (MySQL Connector/NET)
* NUnit (http://www.nunit.org)
* AGEIA Inc. (PhysX)
* Russel L. Smith (ODE)
* Erwin Coumans (Bullet)
* Prebuild (http://sourceforge.net/projects/dnpb/)
* LibOpenMetaverse (http://lib.openmetaverse.org/)
* DotNetOpenMail v0.5.8b (http://dotnetopenmail.sourceforge.net)

7241
OpenSim.FxCop Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,78 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
namespace OpenSim.Groups
{
public class ForeignImporter
{
IUserManagement m_UserManagement;
public ForeignImporter(IUserManagement uman)
{
m_UserManagement = uman;
}
public GroupMembersData ConvertGroupMembersData(ExtendedGroupMembersData _m)
{
GroupMembersData m = new GroupMembersData();
m.AcceptNotices = _m.AcceptNotices;
m.AgentPowers = _m.AgentPowers;
m.Contribution = _m.Contribution;
m.IsOwner = _m.IsOwner;
m.ListInProfile = _m.ListInProfile;
m.OnlineStatus = _m.OnlineStatus;
m.Title = _m.Title;
string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
Util.ParseUniversalUserIdentifier(_m.AgentID, out m.AgentID, out url, out first, out last, out tmp);
if (url != string.Empty)
m_UserManagement.AddUser(m.AgentID, first, last, url);
return m;
}
public GroupRoleMembersData ConvertGroupRoleMembersData(ExtendedGroupRoleMembersData _rm)
{
GroupRoleMembersData rm = new GroupRoleMembersData();
rm.RoleID = _rm.RoleID;
string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
Util.ParseUniversalUserIdentifier(_rm.MemberID, out rm.MemberID, out url, out first, out last, out tmp);
if (url != string.Empty)
m_UserManagement.AddUser(rm.MemberID, first, last, url);
return rm;
}
}
}

View File

@ -1,533 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using OpenSim.Framework;
using OpenMetaverse;
namespace OpenSim.Groups
{
public class ExtendedGroupRecord : GroupRecord
{
public int MemberCount;
public int RoleCount;
public string ServiceLocation;
public string FounderUUI;
}
public class ExtendedGroupMembershipData : GroupMembershipData
{
public string AccessToken;
}
public class ExtendedGroupMembersData
{
// This is the only difference: this is a string
public string AgentID;
public int Contribution;
public string OnlineStatus;
public ulong AgentPowers;
public string Title;
public bool IsOwner;
public bool ListInProfile;
public bool AcceptNotices;
public string AccessToken;
}
public class ExtendedGroupRoleMembersData
{
public UUID RoleID;
// This is the only difference: this is a string
public string MemberID;
}
public struct ExtendedGroupNoticeData
{
public UUID NoticeID;
public uint Timestamp;
public string FromName;
public string Subject;
public bool HasAttachment;
public byte AttachmentType;
public string AttachmentName;
public UUID AttachmentItemID;
public string AttachmentOwnerID;
public GroupNoticeData ToGroupNoticeData()
{
GroupNoticeData n = new GroupNoticeData();
n.FromName = this.FromName;
n.AssetType = this.AttachmentType;
n.HasAttachment = this.HasAttachment;
n.NoticeID = this.NoticeID;
n.Subject = this.Subject;
n.Timestamp = this.Timestamp;
return n;
}
}
public class GroupsDataUtils
{
public static string Sanitize(string s)
{
return s == null ? string.Empty : s;
}
public static Dictionary<string, object> GroupRecord(ExtendedGroupRecord grec)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
if (grec == null)
return dict;
dict["AllowPublish"] = grec.AllowPublish.ToString();
dict["Charter"] = Sanitize(grec.Charter);
dict["FounderID"] = grec.FounderID.ToString();
dict["FounderUUI"] = Sanitize(grec.FounderUUI);
dict["GroupID"] = grec.GroupID.ToString();
dict["GroupName"] = Sanitize(grec.GroupName);
dict["InsigniaID"] = grec.GroupPicture.ToString();
dict["MaturePublish"] = grec.MaturePublish.ToString();
dict["MembershipFee"] = grec.MembershipFee.ToString();
dict["OpenEnrollment"] = grec.OpenEnrollment.ToString();
dict["OwnerRoleID"] = grec.OwnerRoleID.ToString();
dict["ServiceLocation"] = Sanitize(grec.ServiceLocation);
dict["ShownInList"] = grec.ShowInList.ToString();
dict["MemberCount"] = grec.MemberCount.ToString();
dict["RoleCount"] = grec.RoleCount.ToString();
return dict;
}
public static ExtendedGroupRecord GroupRecord(Dictionary<string, object> dict)
{
if (dict == null)
return null;
ExtendedGroupRecord grec = new ExtendedGroupRecord();
if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null)
grec.AllowPublish = bool.Parse(dict["AllowPublish"].ToString());
if (dict.ContainsKey("Charter") && dict["Charter"] != null)
grec.Charter = dict["Charter"].ToString();
else
grec.Charter = string.Empty;
if (dict.ContainsKey("FounderID") && dict["FounderID"] != null)
grec.FounderID = UUID.Parse(dict["FounderID"].ToString());
if (dict.ContainsKey("FounderUUI") && dict["FounderUUI"] != null)
grec.FounderUUI = dict["FounderUUI"].ToString();
else
grec.FounderUUI = string.Empty;
if (dict.ContainsKey("GroupID") && dict["GroupID"] != null)
grec.GroupID = UUID.Parse(dict["GroupID"].ToString());
if (dict.ContainsKey("GroupName") && dict["GroupName"] != null)
grec.GroupName = dict["GroupName"].ToString();
else
grec.GroupName = string.Empty;
if (dict.ContainsKey("InsigniaID") && dict["InsigniaID"] != null)
grec.GroupPicture = UUID.Parse(dict["InsigniaID"].ToString());
if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null)
grec.MaturePublish = bool.Parse(dict["MaturePublish"].ToString());
if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null)
grec.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString());
if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null)
grec.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString());
if (dict.ContainsKey("OwnerRoleID") && dict["OwnerRoleID"] != null)
grec.OwnerRoleID = UUID.Parse(dict["OwnerRoleID"].ToString());
if (dict.ContainsKey("ServiceLocation") && dict["ServiceLocation"] != null)
grec.ServiceLocation = dict["ServiceLocation"].ToString();
else
grec.ServiceLocation = string.Empty;
if (dict.ContainsKey("ShownInList") && dict["ShownInList"] != null)
grec.ShowInList = bool.Parse(dict["ShownInList"].ToString());
if (dict.ContainsKey("MemberCount") && dict["MemberCount"] != null)
grec.MemberCount = Int32.Parse(dict["MemberCount"].ToString());
if (dict.ContainsKey("RoleCount") && dict["RoleCount"] != null)
grec.RoleCount = Int32.Parse(dict["RoleCount"].ToString());
return grec;
}
public static Dictionary<string, object> GroupMembershipData(ExtendedGroupMembershipData membership)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
if (membership == null)
return dict;
dict["AcceptNotices"] = membership.AcceptNotices.ToString();
dict["AccessToken"] = Sanitize(membership.AccessToken);
dict["Active"] = membership.Active.ToString();
dict["ActiveRole"] = membership.ActiveRole.ToString();
dict["AllowPublish"] = membership.AllowPublish.ToString();
dict["Charter"] = Sanitize(membership.Charter);
dict["Contribution"] = membership.Contribution.ToString();
dict["FounderID"] = membership.FounderID.ToString();
dict["GroupID"] = membership.GroupID.ToString();
dict["GroupName"] = Sanitize(membership.GroupName);
dict["GroupPicture"] = membership.GroupPicture.ToString();
dict["GroupPowers"] = membership.GroupPowers.ToString();
dict["GroupTitle"] = Sanitize(membership.GroupTitle);
dict["ListInProfile"] = membership.ListInProfile.ToString();
dict["MaturePublish"] = membership.MaturePublish.ToString();
dict["MembershipFee"] = membership.MembershipFee.ToString();
dict["OpenEnrollment"] = membership.OpenEnrollment.ToString();
dict["ShowInList"] = membership.ShowInList.ToString();
return dict;
}
public static ExtendedGroupMembershipData GroupMembershipData(Dictionary<string, object> dict)
{
if (dict == null)
return null;
ExtendedGroupMembershipData membership = new ExtendedGroupMembershipData();
if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null)
membership.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString());
if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null)
membership.AccessToken = dict["AccessToken"].ToString();
else
membership.AccessToken = string.Empty;
if (dict.ContainsKey("Active") && dict["Active"] != null)
membership.Active = bool.Parse(dict["Active"].ToString());
if (dict.ContainsKey("ActiveRole") && dict["ActiveRole"] != null)
membership.ActiveRole = UUID.Parse(dict["ActiveRole"].ToString());
if (dict.ContainsKey("AllowPublish") && dict["AllowPublish"] != null)
membership.AllowPublish = bool.Parse(dict["AllowPublish"].ToString());
if (dict.ContainsKey("Charter") && dict["Charter"] != null)
membership.Charter = dict["Charter"].ToString();
else
membership.Charter = string.Empty;
if (dict.ContainsKey("Contribution") && dict["Contribution"] != null)
membership.Contribution = Int32.Parse(dict["Contribution"].ToString());
if (dict.ContainsKey("FounderID") && dict["FounderID"] != null)
membership.FounderID = UUID.Parse(dict["FounderID"].ToString());
if (dict.ContainsKey("GroupID") && dict["GroupID"] != null)
membership.GroupID = UUID.Parse(dict["GroupID"].ToString());
if (dict.ContainsKey("GroupName") && dict["GroupName"] != null)
membership.GroupName = dict["GroupName"].ToString();
else
membership.GroupName = string.Empty;
if (dict.ContainsKey("GroupPicture") && dict["GroupPicture"] != null)
membership.GroupPicture = UUID.Parse(dict["GroupPicture"].ToString());
if (dict.ContainsKey("GroupPowers") && dict["GroupPowers"] != null)
membership.GroupPowers = UInt64.Parse(dict["GroupPowers"].ToString());
if (dict.ContainsKey("GroupTitle") && dict["GroupTitle"] != null)
membership.GroupTitle = dict["GroupTitle"].ToString();
else
membership.GroupTitle = string.Empty;
if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null)
membership.ListInProfile = bool.Parse(dict["ListInProfile"].ToString());
if (dict.ContainsKey("MaturePublish") && dict["MaturePublish"] != null)
membership.MaturePublish = bool.Parse(dict["MaturePublish"].ToString());
if (dict.ContainsKey("MembershipFee") && dict["MembershipFee"] != null)
membership.MembershipFee = Int32.Parse(dict["MembershipFee"].ToString());
if (dict.ContainsKey("OpenEnrollment") && dict["OpenEnrollment"] != null)
membership.OpenEnrollment = bool.Parse(dict["OpenEnrollment"].ToString());
if (dict.ContainsKey("ShowInList") && dict["ShowInList"] != null)
membership.ShowInList = bool.Parse(dict["ShowInList"].ToString());
return membership;
}
public static Dictionary<string, object> GroupMembersData(ExtendedGroupMembersData member)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["AcceptNotices"] = member.AcceptNotices.ToString();
dict["AccessToken"] = Sanitize(member.AccessToken);
dict["AgentID"] = Sanitize(member.AgentID);
dict["AgentPowers"] = member.AgentPowers.ToString();
dict["Contribution"] = member.Contribution.ToString();
dict["IsOwner"] = member.IsOwner.ToString();
dict["ListInProfile"] = member.ListInProfile.ToString();
dict["OnlineStatus"] = Sanitize(member.OnlineStatus);
dict["Title"] = Sanitize(member.Title);
return dict;
}
public static ExtendedGroupMembersData GroupMembersData(Dictionary<string, object> dict)
{
ExtendedGroupMembersData member = new ExtendedGroupMembersData();
if (dict == null)
return member;
if (dict.ContainsKey("AcceptNotices") && dict["AcceptNotices"] != null)
member.AcceptNotices = bool.Parse(dict["AcceptNotices"].ToString());
if (dict.ContainsKey("AccessToken") && dict["AccessToken"] != null)
member.AccessToken = Sanitize(dict["AccessToken"].ToString());
else
member.AccessToken = string.Empty;
if (dict.ContainsKey("AgentID") && dict["AgentID"] != null)
member.AgentID = Sanitize(dict["AgentID"].ToString());
else
member.AgentID = UUID.Zero.ToString();
if (dict.ContainsKey("AgentPowers") && dict["AgentPowers"] != null)
member.AgentPowers = UInt64.Parse(dict["AgentPowers"].ToString());
if (dict.ContainsKey("Contribution") && dict["Contribution"] != null)
member.Contribution = Int32.Parse(dict["Contribution"].ToString());
if (dict.ContainsKey("IsOwner") && dict["IsOwner"] != null)
member.IsOwner = bool.Parse(dict["IsOwner"].ToString());
if (dict.ContainsKey("ListInProfile") && dict["ListInProfile"] != null)
member.ListInProfile = bool.Parse(dict["ListInProfile"].ToString());
if (dict.ContainsKey("OnlineStatus") && dict["OnlineStatus"] != null)
member.OnlineStatus = Sanitize(dict["OnlineStatus"].ToString());
else
member.OnlineStatus = string.Empty;
if (dict.ContainsKey("Title") && dict["Title"] != null)
member.Title = Sanitize(dict["Title"].ToString());
else
member.Title = string.Empty;
return member;
}
public static Dictionary<string, object> GroupRolesData(GroupRolesData role)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["Description"] = Sanitize(role.Description);
dict["Members"] = role.Members.ToString();
dict["Name"] = Sanitize(role.Name);
dict["Powers"] = role.Powers.ToString();
dict["RoleID"] = role.RoleID.ToString();
dict["Title"] = Sanitize(role.Title);
return dict;
}
public static GroupRolesData GroupRolesData(Dictionary<string, object> dict)
{
GroupRolesData role = new GroupRolesData();
if (dict == null)
return role;
if (dict.ContainsKey("Description") && dict["Description"] != null)
role.Description = Sanitize(dict["Description"].ToString());
else
role.Description = string.Empty;
if (dict.ContainsKey("Members") && dict["Members"] != null)
role.Members = Int32.Parse(dict["Members"].ToString());
if (dict.ContainsKey("Name") && dict["Name"] != null)
role.Name = Sanitize(dict["Name"].ToString());
else
role.Name = string.Empty;
if (dict.ContainsKey("Powers") && dict["Powers"] != null)
role.Powers = UInt64.Parse(dict["Powers"].ToString());
if (dict.ContainsKey("Title") && dict["Title"] != null)
role.Title = Sanitize(dict["Title"].ToString());
else
role.Title = string.Empty;
if (dict.ContainsKey("RoleID") && dict["RoleID"] != null)
role.RoleID = UUID.Parse(dict["RoleID"].ToString());
return role;
}
public static Dictionary<string, object> GroupRoleMembersData(ExtendedGroupRoleMembersData rmember)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["RoleID"] = rmember.RoleID.ToString();
dict["MemberID"] = rmember.MemberID;
return dict;
}
public static ExtendedGroupRoleMembersData GroupRoleMembersData(Dictionary<string, object> dict)
{
ExtendedGroupRoleMembersData rmember = new ExtendedGroupRoleMembersData();
if (dict.ContainsKey("RoleID") && dict["RoleID"] != null)
rmember.RoleID = new UUID(dict["RoleID"].ToString());
if (dict.ContainsKey("MemberID") && dict["MemberID"] != null)
rmember.MemberID = dict["MemberID"].ToString();
return rmember;
}
public static Dictionary<string, object> GroupInviteInfo(GroupInviteInfo invite)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["InviteID"] = invite.InviteID.ToString();
dict["GroupID"] = invite.GroupID.ToString();
dict["RoleID"] = invite.RoleID.ToString();
dict["AgentID"] = invite.AgentID;
return dict;
}
public static GroupInviteInfo GroupInviteInfo(Dictionary<string, object> dict)
{
if (dict == null)
return null;
GroupInviteInfo invite = new GroupInviteInfo();
invite.InviteID = new UUID(dict["InviteID"].ToString());
invite.GroupID = new UUID(dict["GroupID"].ToString());
invite.RoleID = new UUID(dict["RoleID"].ToString());
invite.AgentID = Sanitize(dict["AgentID"].ToString());
return invite;
}
public static Dictionary<string, object> GroupNoticeData(ExtendedGroupNoticeData notice)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["NoticeID"] = notice.NoticeID.ToString();
dict["Timestamp"] = notice.Timestamp.ToString();
dict["FromName"] = Sanitize(notice.FromName);
dict["Subject"] = Sanitize(notice.Subject);
dict["HasAttachment"] = notice.HasAttachment.ToString();
dict["AttachmentItemID"] = notice.AttachmentItemID.ToString();
dict["AttachmentName"] = Sanitize(notice.AttachmentName);
dict["AttachmentType"] = notice.AttachmentType.ToString();
dict["AttachmentOwnerID"] = Sanitize(notice.AttachmentOwnerID);
return dict;
}
public static ExtendedGroupNoticeData GroupNoticeData(Dictionary<string, object> dict)
{
ExtendedGroupNoticeData notice = new ExtendedGroupNoticeData();
if (dict == null)
return notice;
notice.NoticeID = new UUID(dict["NoticeID"].ToString());
notice.Timestamp = UInt32.Parse(dict["Timestamp"].ToString());
notice.FromName = Sanitize(dict["FromName"].ToString());
notice.Subject = Sanitize(dict["Subject"].ToString());
notice.HasAttachment = bool.Parse(dict["HasAttachment"].ToString());
notice.AttachmentItemID = new UUID(dict["AttachmentItemID"].ToString());
notice.AttachmentName = dict["AttachmentName"].ToString();
notice.AttachmentType = byte.Parse(dict["AttachmentType"].ToString());
notice.AttachmentOwnerID = dict["AttachmentOwnerID"].ToString();
return notice;
}
public static Dictionary<string, object> GroupNoticeInfo(GroupNoticeInfo notice)
{
Dictionary<string, object> dict = GroupNoticeData(notice.noticeData);
dict["GroupID"] = notice.GroupID.ToString();
dict["Message"] = Sanitize(notice.Message);
return dict;
}
public static GroupNoticeInfo GroupNoticeInfo(Dictionary<string, object> dict)
{
GroupNoticeInfo notice = new GroupNoticeInfo();
notice.noticeData = GroupNoticeData(dict);
notice.GroupID = new UUID(dict["GroupID"].ToString());
notice.Message = Sanitize(dict["Message"].ToString());
return notice;
}
public static Dictionary<string, object> DirGroupsReplyData(DirGroupsReplyData g)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["GroupID"] = g.groupID;
dict["Name"] = g.groupName;
dict["NMembers"] = g.members;
dict["SearchOrder"] = g.searchOrder;
return dict;
}
public static DirGroupsReplyData DirGroupsReplyData(Dictionary<string, object> dict)
{
DirGroupsReplyData g;
g.groupID = new UUID(dict["GroupID"].ToString());
g.groupName = dict["Name"].ToString();
Int32.TryParse(dict["NMembers"].ToString(), out g.members);
float.TryParse(dict["SearchOrder"].ToString(), out g.searchOrder);
return g;
}
}
}

View File

@ -1,841 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using log4net;
using Mono.Addins;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
namespace OpenSim.Groups
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsMessagingModule")]
public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private List<Scene> m_sceneList = new List<Scene>();
private IPresenceService m_presenceService;
private IMessageTransferModule m_msgTransferModule = null;
private IUserManagement m_UserManagement = null;
private IGroupsServicesConnector m_groupData = null;
// Config Options
private bool m_groupMessagingEnabled;
private bool m_debugEnabled;
/// <summary>
/// If enabled, module only tries to send group IMs to online users by querying cached presence information.
/// </summary>
private bool m_messageOnlineAgentsOnly;
/// <summary>
/// Cache for online users.
/// </summary>
/// <remarks>
/// Group ID is key, presence information for online members is value.
/// Will only be non-null if m_messageOnlineAgentsOnly = true
/// We cache here so that group messages don't constantly have to re-request the online user list to avoid
/// attempted expensive sending of messages to offline users.
/// The tradeoff is that a user that comes online will not receive messages consistently from all other users
/// until caches have updated.
/// Therefore, we set the cache expiry to just 20 seconds.
/// </remarks>
private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
private int m_usersOnlineCacheExpirySeconds = 20;
private Dictionary<UUID, List<string>> m_groupsAgentsDroppedFromChatSession = new Dictionary<UUID, List<string>>();
private Dictionary<UUID, List<string>> m_groupsAgentsInvitedToChatSession = new Dictionary<UUID, List<string>>();
#region Region Module interfaceBase Members
public void Initialise(IConfigSource config)
{
IConfig groupsConfig = config.Configs["Groups"];
if (groupsConfig == null)
// Do not run this module by default.
return;
// if groups aren't enabled, we're not needed.
// if we're not specified as the connector to use, then we're not wanted
if ((groupsConfig.GetBoolean("Enabled", false) == false)
|| (groupsConfig.GetString("MessagingModule", "") != Name))
{
m_groupMessagingEnabled = false;
return;
}
m_groupMessagingEnabled = groupsConfig.GetBoolean("MessagingEnabled", true);
if (!m_groupMessagingEnabled)
return;
m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
if (m_messageOnlineAgentsOnly)
{
m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
}
else
{
m_log.Error("[Groups.Messaging]: GroupsMessagingModule V2 requires MessageOnlineUsersOnly = true");
m_groupMessagingEnabled = false;
return;
}
m_debugEnabled = groupsConfig.GetBoolean("MessagingDebugEnabled", m_debugEnabled);
m_log.InfoFormat(
"[Groups.Messaging]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
m_messageOnlineAgentsOnly, m_debugEnabled);
}
public void AddRegion(Scene scene)
{
if (!m_groupMessagingEnabled)
return;
scene.RegisterModuleInterface<IGroupsMessagingModule>(this);
m_sceneList.Add(scene);
scene.EventManager.OnNewClient += OnNewClient;
scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
scene.EventManager.OnClientLogin += OnClientLogin;
scene.AddCommand(
"Debug",
this,
"debug groups messaging verbose",
"debug groups messaging verbose <true|false>",
"This setting turns on very verbose groups messaging debugging",
HandleDebugGroupsMessagingVerbose);
}
public void RegionLoaded(Scene scene)
{
if (!m_groupMessagingEnabled)
return;
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
m_groupData = scene.RequestModuleInterface<IGroupsServicesConnector>();
// No groups module, no groups messaging
if (m_groupData == null)
{
m_log.Error("[Groups.Messaging]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled.");
RemoveRegion(scene);
return;
}
m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
// No message transfer module, no groups messaging
if (m_msgTransferModule == null)
{
m_log.Error("[Groups.Messaging]: Could not get MessageTransferModule");
RemoveRegion(scene);
return;
}
m_UserManagement = scene.RequestModuleInterface<IUserManagement>();
// No groups module, no groups messaging
if (m_UserManagement == null)
{
m_log.Error("[Groups.Messaging]: Could not get IUserManagement, GroupsMessagingModule is now disabled.");
RemoveRegion(scene);
return;
}
if (m_presenceService == null)
m_presenceService = scene.PresenceService;
}
public void RemoveRegion(Scene scene)
{
if (!m_groupMessagingEnabled)
return;
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
m_sceneList.Remove(scene);
scene.EventManager.OnNewClient -= OnNewClient;
scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
scene.EventManager.OnClientLogin -= OnClientLogin;
scene.UnregisterModuleInterface<IGroupsMessagingModule>(this);
}
public void Close()
{
if (!m_groupMessagingEnabled)
return;
if (m_debugEnabled) m_log.Debug("[Groups.Messaging]: Shutting down GroupsMessagingModule module.");
m_sceneList.Clear();
m_groupData = null;
m_msgTransferModule = null;
}
public Type ReplaceableInterface
{
get { return null; }
}
public string Name
{
get { return "Groups Messaging Module V2"; }
}
public void PostInitialise()
{
// NoOp
}
#endregion
private void HandleDebugGroupsMessagingVerbose(object modules, string[] args)
{
if (args.Length < 5)
{
MainConsole.Instance.Output("Usage: debug groups messaging verbose <true|false>");
return;
}
bool verbose = false;
if (!bool.TryParse(args[4], out verbose))
{
MainConsole.Instance.Output("Usage: debug groups messaging verbose <true|false>");
return;
}
m_debugEnabled = verbose;
MainConsole.Instance.Output("{0} verbose logging set to {1}", Name, m_debugEnabled);
}
/// <summary>
/// Not really needed, but does confirm that the group exists.
/// </summary>
public bool StartGroupChatSession(UUID agentID, UUID groupID)
{
if (m_debugEnabled)
m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null);
if (groupInfo != null)
{
return true;
}
else
{
return false;
}
}
public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
{
SendMessageToGroup(im, groupID, UUID.Zero, null);
}
public void SendMessageToGroup(
GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func<GroupMembersData, bool> sendCondition)
{
int requestStartTick = Environment.TickCount;
UUID fromAgentID = new UUID(im.fromAgentID);
// Unlike current XmlRpcGroups, Groups V2 can accept UUID.Zero when a perms check for the requesting agent
// is not necessary.
List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(UUID.Zero.ToString(), groupID);
int groupMembersCount = groupMembers.Count;
PresenceInfo[] onlineAgents = null;
// In V2 we always only send to online members.
// Sending to offline members is not an option.
string[] t1 = groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()).ToArray();
// We cache in order not to overwhelm the presence service on large grids with many groups. This does
// mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed.
// (assuming this is the same across all grid simulators).
if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
{
onlineAgents = m_presenceService.GetAgents(t1);
m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
}
HashSet<string> onlineAgentsUuidSet = new HashSet<string>();
Array.ForEach<PresenceInfo>(onlineAgents, pi => onlineAgentsUuidSet.Add(pi.UserID));
groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
// if (m_debugEnabled)
// m_log.DebugFormat(
// "[Groups.Messaging]: SendMessageToGroup called for group {0} with {1} visible members, {2} online",
// groupID, groupMembersCount, groupMembers.Count());
im.imSessionID = groupID.Guid;
im.fromGroup = true;
IClientAPI thisClient = GetActiveClient(fromAgentID);
if (thisClient != null)
{
im.RegionID = thisClient.Scene.RegionInfo.RegionID.Guid;
}
if ((im.binaryBucket == null) || (im.binaryBucket.Length == 0) || ((im.binaryBucket.Length == 1 && im.binaryBucket[0] == 0)))
{
ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), groupID, null);
if (groupInfo != null)
im.binaryBucket = Util.StringToBytes256(groupInfo.GroupName);
}
// Send to self first of all
im.toAgentID = im.fromAgentID;
im.fromGroup = true;
ProcessMessageFromGroupSession(im);
List<UUID> regions = new List<UUID>();
List<UUID> clientsAlreadySent = new List<UUID>();
// Then send to everybody else
foreach (GroupMembersData member in groupMembers)
{
if (member.AgentID.Guid == im.fromAgentID)
continue;
if (clientsAlreadySent.Contains(member.AgentID))
continue;
clientsAlreadySent.Add(member.AgentID);
if (sendCondition != null)
{
if (!sendCondition(member))
{
if (m_debugEnabled)
m_log.DebugFormat(
"[Groups.Messaging]: Not sending to {0} as they do not fulfill send condition",
member.AgentID);
continue;
}
}
else if (hasAgentDroppedGroupChatSession(member.AgentID.ToString(), groupID))
{
// Don't deliver messages to people who have dropped this session
if (m_debugEnabled)
m_log.DebugFormat("[Groups.Messaging]: {0} has dropped session, not delivering to them", member.AgentID);
continue;
}
im.toAgentID = member.AgentID.Guid;
IClientAPI client = GetActiveClient(member.AgentID);
if (client == null)
{
// If they're not local, forward across the grid
// BUT do it only once per region, please! Sim would be even better!
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} via Grid", member.AgentID);
bool reallySend = true;
if (onlineAgents != null)
{
PresenceInfo presence = onlineAgents.First(p => p.UserID == member.AgentID.ToString());
if (regions.Contains(presence.RegionID))
reallySend = false;
else
regions.Add(presence.RegionID);
}
if (reallySend)
{
// We have to create a new IM structure because the transfer module
// uses async send
GridInstantMessage msg = new GridInstantMessage(im, true);
m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { });
}
}
else
{
// Deliver locally, directly
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name);
ProcessMessageFromGroupSession(im);
}
}
if (m_debugEnabled)
m_log.DebugFormat(
"[Groups.Messaging]: SendMessageToGroup for group {0} with {1} visible members, {2} online took {3}ms",
groupID, groupMembersCount, groupMembers.Count(), Environment.TickCount - requestStartTick);
}
#region SimGridEventHandlers
void OnClientLogin(IClientAPI client)
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name);
}
private void OnNewClient(IClientAPI client)
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: OnInstantMessage registered for {0}", client.Name);
ResetAgentGroupChatSessions(client.AgentId.ToString());
}
void OnMakeRootAgent(ScenePresence sp)
{
sp.ControllingClient.OnInstantMessage += OnInstantMessage;
}
void OnMakeChildAgent(ScenePresence sp)
{
sp.ControllingClient.OnInstantMessage -= OnInstantMessage;
}
private void OnGridInstantMessage(GridInstantMessage msg)
{
// The instant message module will only deliver messages of dialog types:
// MessageFromAgent, StartTyping, StopTyping, MessageFromObject
//
// Any other message type will not be delivered to a client by the
// Instant Message Module
UUID regionID = new UUID(msg.RegionID);
if (m_debugEnabled)
{
m_log.DebugFormat("[Groups.Messaging]: {0} called, IM from region {1}",
System.Reflection.MethodBase.GetCurrentMethod().Name, regionID);
DebugGridInstantMessage(msg);
}
// Incoming message from a group
if ((msg.fromGroup == true) && (msg.dialog == (byte)InstantMessageDialog.SessionSend))
{
// We have to redistribute the message across all members of the group who are here
// on this sim
UUID GroupID = new UUID(msg.imSessionID);
Scene aScene = m_sceneList[0];
GridRegion regionOfOrigin = aScene.GridService.GetRegionByUUID(aScene.RegionInfo.ScopeID, regionID);
List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(UUID.Zero.ToString(), GroupID);
//if (m_debugEnabled)
// foreach (GroupMembersData m in groupMembers)
// m_log.DebugFormat("[Groups.Messaging]: member {0}", m.AgentID);
foreach (Scene s in m_sceneList)
{
s.ForEachScenePresence(sp =>
{
// If we got this via grid messaging, it's because the caller thinks
// that the root agent is here. We should only send the IM to root agents.
if (sp.IsChildAgent)
return;
GroupMembersData m = groupMembers.Find(gmd =>
{
return gmd.AgentID == sp.UUID;
});
if (m.AgentID == UUID.Zero)
{
if (m_debugEnabled)
m_log.DebugFormat("[Groups.Messaging]: skipping agent {0} because he is not a member of the group", sp.UUID);
return;
}
// Check if the user has an agent in the region where
// the IM came from, and if so, skip it, because the IM
// was already sent via that agent
if (regionOfOrigin != null)
{
AgentCircuitData aCircuit = s.AuthenticateHandler.GetAgentCircuitData(sp.UUID);
if (aCircuit != null)
{
if (aCircuit.ChildrenCapSeeds.Keys.Contains(regionOfOrigin.RegionHandle))
{
if (m_debugEnabled)
m_log.DebugFormat("[Groups.Messaging]: skipping agent {0} because he has an agent in region of origin", sp.UUID);
return;
}
else
{
if (m_debugEnabled)
m_log.DebugFormat("[Groups.Messaging]: not skipping agent {0}", sp.UUID);
}
}
}
UUID AgentID = sp.UUID;
msg.toAgentID = AgentID.Guid;
if (!hasAgentDroppedGroupChatSession(AgentID.ToString(), GroupID))
{
if (!hasAgentBeenInvitedToGroupChatSession(AgentID.ToString(), GroupID))
AddAgentToSession(AgentID, GroupID, msg);
else
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", sp.Name);
ProcessMessageFromGroupSession(msg);
}
}
});
}
}
}
private void ProcessMessageFromGroupSession(GridInstantMessage msg)
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID);
UUID AgentID = new UUID(msg.fromAgentID);
UUID GroupID = new UUID(msg.imSessionID);
UUID toAgentID = new UUID(msg.toAgentID);
switch (msg.dialog)
{
case (byte)InstantMessageDialog.SessionAdd:
AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
break;
case (byte)InstantMessageDialog.SessionDrop:
AgentDroppedFromGroupChatSession(AgentID.ToString(), GroupID);
break;
case (byte)InstantMessageDialog.SessionSend:
// User hasn't dropped, so they're in the session,
// maybe we should deliver it.
IClientAPI client = GetActiveClient(new UUID(msg.toAgentID));
if (client != null)
{
// Deliver locally, directly
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Delivering to {0} locally", client.Name);
if (!hasAgentDroppedGroupChatSession(toAgentID.ToString(), GroupID))
{
if (!hasAgentBeenInvitedToGroupChatSession(toAgentID.ToString(), GroupID))
// This actually sends the message too, so no need to resend it
// with client.SendInstantMessage
AddAgentToSession(toAgentID, GroupID, msg);
else
client.SendInstantMessage(msg);
}
}
else
{
m_log.WarnFormat("[Groups.Messaging]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
}
break;
default:
m_log.WarnFormat("[Groups.Messaging]: I don't know how to proccess a {0} message.", ((InstantMessageDialog)msg.dialog).ToString());
break;
}
}
private void AddAgentToSession(UUID AgentID, UUID GroupID, GridInstantMessage msg)
{
// Agent not in session and hasn't dropped from session
// Add them to the session for now, and Invite them
AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
IClientAPI activeClient = GetActiveClient(AgentID);
if (activeClient != null)
{
GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
if (groupInfo != null)
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Sending chatterbox invite instant message");
UUID fromAgent = new UUID(msg.fromAgentID);
// Force? open the group session dialog???
// and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg);
IEventQueue eq = activeClient.Scene.RequestModuleInterface<IEventQueue>();
if (eq != null)
{
eq.ChatterboxInvitation(
GroupID
, groupInfo.GroupName
, fromAgent
, msg.message
, AgentID
, msg.fromAgentName
, msg.dialog
, msg.timestamp
, msg.offline == 1
, (int)msg.ParentEstateID
, msg.Position
, 1
, new UUID(msg.imSessionID)
, msg.fromGroup
, OpenMetaverse.Utils.StringToBytes(groupInfo.GroupName)
);
var update = new GroupChatListAgentUpdateData(AgentID);
var updates = new List<GroupChatListAgentUpdateData> { update };
eq.ChatterBoxSessionAgentListUpdates(GroupID, new UUID(msg.toAgentID), updates);
}
}
}
}
#endregion
#region ClientEvents
private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
{
if (m_debugEnabled)
{
m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
DebugGridInstantMessage(im);
}
// Start group IM session
if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart))
{
if (m_debugEnabled) m_log.InfoFormat("[Groups.Messaging]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID);
UUID GroupID = new UUID(im.imSessionID);
UUID AgentID = new UUID(im.fromAgentID);
GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null);
if (groupInfo != null)
{
AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID);
IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
if (queue != null)
{
var update = new GroupChatListAgentUpdateData(AgentID);
var updates = new List<GroupChatListAgentUpdateData> { update };
queue.ChatterBoxSessionAgentListUpdates(GroupID, remoteClient.AgentId, updates);
}
}
}
// Send a message from locally connected client to a group
if ((im.dialog == (byte)InstantMessageDialog.SessionSend))
{
UUID GroupID = new UUID(im.imSessionID);
UUID AgentID = new UUID(im.fromAgentID);
if (m_debugEnabled)
m_log.DebugFormat("[Groups.Messaging]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString());
//If this agent is sending a message, then they want to be in the session
AgentInvitedToGroupChatSession(AgentID.ToString(), GroupID);
SendMessageToGroup(im, GroupID);
}
}
#endregion
void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID)
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
OSDMap moderatedMap = new OSDMap(4);
moderatedMap.Add("voice", OSD.FromBoolean(false));
OSDMap sessionMap = new OSDMap(4);
sessionMap.Add("moderated_mode", moderatedMap);
sessionMap.Add("session_name", OSD.FromString(groupName));
sessionMap.Add("type", OSD.FromInteger(0));
sessionMap.Add("voice_enabled", OSD.FromBoolean(false));
OSDMap bodyMap = new OSDMap(4);
bodyMap.Add("session_id", OSD.FromUUID(groupID));
bodyMap.Add("temp_session_id", OSD.FromUUID(groupID));
bodyMap.Add("success", OSD.FromBoolean(true));
bodyMap.Add("session_info", sessionMap);
IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
queue?.Enqueue(queue.BuildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId);
}
private void DebugGridInstantMessage(GridInstantMessage im)
{
// Don't log any normal IMs (privacy!)
if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent)
{
m_log.WarnFormat("[Groups.Messaging]: IM: fromGroup({0})", im.fromGroup ? "True" : "False");
m_log.WarnFormat("[Groups.Messaging]: IM: Dialog({0})", ((InstantMessageDialog)im.dialog).ToString());
m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentID({0})", im.fromAgentID.ToString());
m_log.WarnFormat("[Groups.Messaging]: IM: fromAgentName({0})", im.fromAgentName.ToString());
m_log.WarnFormat("[Groups.Messaging]: IM: imSessionID({0})", im.imSessionID.ToString());
m_log.WarnFormat("[Groups.Messaging]: IM: message({0})", im.message.ToString());
m_log.WarnFormat("[Groups.Messaging]: IM: offline({0})", im.offline.ToString());
m_log.WarnFormat("[Groups.Messaging]: IM: toAgentID({0})", im.toAgentID.ToString());
m_log.WarnFormat("[Groups.Messaging]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket"));
}
}
#region Client Tools
/// <summary>
/// Try to find an active IClientAPI reference for agentID giving preference to root connections
/// </summary>
private IClientAPI GetActiveClient(UUID agentID)
{
if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Looking for local client {0}", agentID);
IClientAPI child = null;
// Try root avatar first
foreach (Scene scene in m_sceneList)
{
ScenePresence sp = scene.GetScenePresence(agentID);
if (sp != null)
{
if (!sp.IsChildAgent)
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Found root agent for client : {0}", sp.ControllingClient.Name);
return sp.ControllingClient;
}
else
{
if (m_debugEnabled) m_log.DebugFormat("[Groups.Messaging]: Found child agent for client : {0}", sp.ControllingClient.Name);
child = sp.ControllingClient;
}
}
}
// If we didn't find a root, then just return whichever child we found, or null if none
if (child == null)
{
if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Could not find local client for agent : {0}", agentID);
}
else
{
if (m_debugEnabled) m_log.WarnFormat("[Groups.Messaging]: Returning child agent for client : {0}", child.Name);
}
return child;
}
#endregion
#region GroupSessionTracking
public void ResetAgentGroupChatSessions(string agentID)
{
foreach (List<string> agentList in m_groupsAgentsDroppedFromChatSession.Values)
agentList.Remove(agentID);
foreach (List<string> agentList in m_groupsAgentsInvitedToChatSession.Values)
agentList.Remove(agentID);
}
public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID)
{
// If we're tracking this group, and we can find them in the tracking, then they've been invited
return m_groupsAgentsInvitedToChatSession.ContainsKey(groupID)
&& m_groupsAgentsInvitedToChatSession[groupID].Contains(agentID);
}
public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID)
{
// If we're tracking drops for this group,
// and we find them, well... then they've dropped
return m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID)
&& m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID);
}
public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID)
{
if (m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID))
{
// If not in dropped list, add
if (!m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID))
{
m_groupsAgentsDroppedFromChatSession[groupID].Add(agentID);
}
}
}
public void AgentInvitedToGroupChatSession(string agentID, UUID groupID)
{
// Add Session Status if it doesn't exist for this session
CreateGroupChatSessionTracking(groupID);
// If nessesary, remove from dropped list
if (m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID))
{
m_groupsAgentsDroppedFromChatSession[groupID].Remove(agentID);
}
// Add to invited
if (!m_groupsAgentsInvitedToChatSession[groupID].Contains(agentID))
m_groupsAgentsInvitedToChatSession[groupID].Add(agentID);
}
private void CreateGroupChatSessionTracking(UUID groupID)
{
if (!m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID))
{
m_groupsAgentsDroppedFromChatSession.Add(groupID, new List<string>());
m_groupsAgentsInvitedToChatSession.Add(groupID, new List<string>());
}
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,289 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using OpenSim.Framework;
using OpenSim.Server.Base;
using OpenMetaverse;
using log4net;
namespace OpenSim.Groups
{
public class GroupsServiceHGConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private string m_ServerURI;
private object m_Lock = new object();
public GroupsServiceHGConnector(string url)
{
m_ServerURI = url;
if (!m_ServerURI.EndsWith("/"))
m_ServerURI += "/";
m_log.DebugFormat("[Groups.HGConnector]: Groups server at {0}", m_ServerURI);
}
public bool CreateProxy(string RequestingAgentID, string AgentID, string accessToken, UUID groupID, string url, string name, out string reason)
{
reason = string.Empty;
Dictionary<string, object> sendData = new Dictionary<string,object>();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["AgentID"] = AgentID.ToString();
sendData["AccessToken"] = accessToken;
sendData["GroupID"] = groupID.ToString();
sendData["Location"] = url;
sendData["Name"] = name;
Dictionary<string, object> ret = MakeRequest("POSTGROUP", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
{
reason = ret["REASON"].ToString();
return false;
}
return true;
}
public void RemoveAgentFromGroup(string AgentID, UUID GroupID, string token)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID;
sendData["GroupID"] = GroupID.ToString();
sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
MakeRequest("REMOVEAGENTFROMGROUP", sendData);
}
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName, string token)
{
if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty)))
return null;
Dictionary<string, object> sendData = new Dictionary<string, object>();
if (GroupID != UUID.Zero)
sendData["GroupID"] = GroupID.ToString();
if (!string.IsNullOrEmpty(GroupName))
sendData["Name"] = GroupsDataUtils.Sanitize(GroupName);
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
Dictionary<string, object> ret = MakeRequest("GETGROUP", sendData);
if (ret == null)
return null;
if (!ret.ContainsKey("RESULT"))
return null;
if (ret["RESULT"].ToString() == "NULL")
return null;
return GroupsDataUtils.GroupRecord((Dictionary<string, object>)ret["RESULT"]);
}
public List<ExtendedGroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
{
List<ExtendedGroupMembersData> members = new List<ExtendedGroupMembersData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
Dictionary<string, object> ret = MakeRequest("GETGROUPMEMBERS", sendData);
if (ret == null)
return members;
if (!ret.ContainsKey("RESULT"))
return members;
if (ret["RESULT"].ToString() == "NULL")
return members;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary<string, object>)v);
members.Add(m);
}
return members;
}
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
{
List<GroupRolesData> roles = new List<GroupRolesData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
Dictionary<string, object> ret = MakeRequest("GETGROUPROLES", sendData);
if (ret == null)
return roles;
if (!ret.ContainsKey("RESULT"))
return roles;
if (ret["RESULT"].ToString() == "NULL")
return roles;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary<string, object>)v);
roles.Add(m);
}
return roles;
}
public List<ExtendedGroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
{
List<ExtendedGroupRoleMembersData> rmembers = new List<ExtendedGroupRoleMembersData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["AccessToken"] = GroupsDataUtils.Sanitize(token);
Dictionary<string, object> ret = MakeRequest("GETROLEMEMBERS", sendData);
if (ret == null)
return rmembers;
if (!ret.ContainsKey("RESULT"))
return rmembers;
if (ret["RESULT"].ToString() == "NULL")
return rmembers;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary<string, object>)v);
rmembers.Add(m);
}
return rmembers;
}
public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = groupID.ToString();
sendData["NoticeID"] = noticeID.ToString();
sendData["FromName"] = GroupsDataUtils.Sanitize(fromName);
sendData["Subject"] = GroupsDataUtils.Sanitize(subject);
sendData["Message"] = GroupsDataUtils.Sanitize(message);
sendData["HasAttachment"] = hasAttachment.ToString();
if (hasAttachment)
{
sendData["AttachmentType"] = attType.ToString();
sendData["AttachmentName"] = attName.ToString();
sendData["AttachmentItemID"] = attItemID.ToString();
sendData["AttachmentOwnerID"] = attOwnerID;
}
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("ADDNOTICE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
return false;
return true;
}
public bool VerifyNotice(UUID noticeID, UUID groupID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["NoticeID"] = noticeID.ToString();
sendData["GroupID"] = groupID.ToString();
Dictionary<string, object> ret = MakeRequest("VERIFYNOTICE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
return false;
return true;
}
//
//
//
//
//
#region Make Request
private Dictionary<string, object> MakeRequest(string method, Dictionary<string, object> sendData)
{
sendData["METHOD"] = method;
string reply = string.Empty;
lock (m_Lock)
reply = SynchronousRestFormsRequester.MakeRequest("POST",
m_ServerURI + "hg-groups",
ServerUtils.BuildQueryString(sendData));
//m_log.DebugFormat("[XXX]: reply was {0}", reply);
if (string.IsNullOrEmpty(reply))
return null;
Dictionary<string, object> replyData = ServerUtils.ParseXmlResponse(
reply);
return replyData;
}
#endregion
}
}

View File

@ -1,695 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using OpenSim.Framework;
using OpenSim.Framework.Monitoring;
using OpenSim.Framework.Servers;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
using OpenMetaverse;
using Mono.Addins;
using log4net;
using Nini.Config;
namespace OpenSim.Groups
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceHGConnectorModule")]
public class GroupsServiceHGConnectorModule : ISharedRegionModule, IGroupsServicesConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private bool m_Enabled = false;
private IGroupsServicesConnector m_LocalGroupsConnector;
private string m_LocalGroupsServiceLocation;
private IUserManagement m_UserManagement;
private IOfflineIMService m_OfflineIM;
private IMessageTransferModule m_Messaging;
private List<Scene> m_Scenes;
private ForeignImporter m_ForeignImporter;
private string m_ServiceLocation;
private IConfigSource m_Config;
private Dictionary<string, GroupsServiceHGConnector> m_NetworkConnectors = new Dictionary<string, GroupsServiceHGConnector>();
private RemoteConnectorCacheWrapper m_CacheWrapper; // for caching info of external group services
#region ISharedRegionModule
public void Initialise(IConfigSource config)
{
IConfig groupsConfig = config.Configs["Groups"];
if (groupsConfig == null)
return;
if ((groupsConfig.GetBoolean("Enabled", false) == false)
|| (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name))
{
return;
}
m_Config = config;
m_ServiceLocation = groupsConfig.GetString("LocalService", "local"); // local or remote
m_LocalGroupsServiceLocation = groupsConfig.GetString("GroupsExternalURI", "http://127.0.0.1");
m_Scenes = new List<Scene>();
m_Enabled = true;
m_log.DebugFormat("[Groups]: Initializing {0} with LocalService {1}", this.Name, m_ServiceLocation);
}
public string Name
{
get { return "Groups HG Service Connector"; }
}
public Type ReplaceableInterface
{
get { return null; }
}
public void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName);
scene.RegisterModuleInterface<IGroupsServicesConnector>(this);
m_Scenes.Add(scene);
scene.EventManager.OnNewClient += OnNewClient;
}
public void RemoveRegion(Scene scene)
{
if (!m_Enabled)
return;
scene.UnregisterModuleInterface<IGroupsServicesConnector>(this);
m_Scenes.Remove(scene);
}
public void RegionLoaded(Scene scene)
{
if (!m_Enabled)
return;
if (m_UserManagement == null)
{
m_UserManagement = scene.RequestModuleInterface<IUserManagement>();
m_OfflineIM = scene.RequestModuleInterface<IOfflineIMService>();
m_Messaging = scene.RequestModuleInterface<IMessageTransferModule>();
m_ForeignImporter = new ForeignImporter(m_UserManagement);
if (m_ServiceLocation.Equals("local"))
{
m_LocalGroupsConnector = new GroupsServiceLocalConnectorModule(m_Config, m_UserManagement);
// Also, if local, create the endpoint for the HGGroupsService
new HGGroupsServiceRobustConnector(m_Config, MainServer.Instance, string.Empty,
scene.RequestModuleInterface<IOfflineIMService>(), scene.RequestModuleInterface<IUserAccountService>());
}
else
m_LocalGroupsConnector = new GroupsServiceRemoteConnectorModule(m_Config, m_UserManagement);
m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement);
}
}
public void PostInitialise()
{
}
public void Close()
{
}
#endregion
private void OnNewClient(IClientAPI client)
{
client.OnCompleteMovementToRegion += OnCompleteMovementToRegion;
}
void OnCompleteMovementToRegion(IClientAPI client, bool arg2)
{
object sp = null;
if (client.Scene.TryGetScenePresence(client.AgentId, out sp))
{
if (sp is ScenePresence && ((ScenePresence)sp).PresenceType != PresenceType.Npc)
{
AgentCircuitData aCircuit = ((ScenePresence)sp).Scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId);
if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 &&
m_OfflineIM != null && m_Messaging != null)
{
List<GridInstantMessage> ims = m_OfflineIM.GetMessages(aCircuit.AgentID);
if (ims != null && ims.Count > 0)
foreach (GridInstantMessage im in ims)
m_Messaging.SendInstantMessage(im, delegate(bool success) { });
}
}
}
}
#region IGroupsServicesConnector
public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment,
bool allowPublish, bool maturePublish, UUID founderID, out string reason)
{
reason = string.Empty;
if (m_UserManagement.IsLocalGridUser(RequestingAgentID))
return m_LocalGroupsConnector.CreateGroup(RequestingAgentID, name, charter, showInList, insigniaID,
membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason);
else
{
reason = "Only local grid users are allowed to create a new group";
return UUID.Zero;
}
}
public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee,
bool openEnrollment, bool allowPublish, bool maturePublish, out string reason)
{
reason = string.Empty;
string url = string.Empty;
string name = string.Empty;
if (IsLocal(groupID, out url, out name))
return m_LocalGroupsConnector.UpdateGroup(AgentUUI(RequestingAgentID), groupID, charter, showInList, insigniaID, membershipFee,
openEnrollment, allowPublish, maturePublish, out reason);
else
{
reason = "Changes to remote group not allowed. Please go to the group's original world.";
return false;
}
}
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName)
{
string url = string.Empty;
string name = string.Empty;
if (IsLocal(GroupID, out url, out name))
return m_LocalGroupsConnector.GetGroupRecord(AgentUUI(RequestingAgentID), GroupID, GroupName);
else if (url != string.Empty)
{
ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID);
string accessToken = string.Empty;
if (membership != null)
accessToken = membership.AccessToken;
else
return null;
GroupsServiceHGConnector c = GetConnector(url);
if (c != null)
{
ExtendedGroupRecord grec = m_CacheWrapper.GetGroupRecord(RequestingAgentID, GroupID, GroupName, delegate
{
return c.GetGroupRecord(AgentUUIForOutside(RequestingAgentID), GroupID, GroupName, accessToken);
});
if (grec != null)
ImportForeigner(grec.FounderUUI);
return grec;
}
}
return null;
}
public List<DirGroupsReplyData> FindGroups(string RequestingAgentIDstr, string search)
{
return m_LocalGroupsConnector.FindGroups(RequestingAgentIDstr, search);
}
public List<GroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(GroupID, out url, out gname))
{
string agentID = AgentUUI(RequestingAgentID);
return m_LocalGroupsConnector.GetGroupMembers(agentID, GroupID);
}
else if (!string.IsNullOrEmpty(url))
{
ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, GroupID);
string accessToken = string.Empty;
if (membership != null)
accessToken = membership.AccessToken;
else
return null;
GroupsServiceHGConnector c = GetConnector(url);
if (c != null)
{
return m_CacheWrapper.GetGroupMembers(RequestingAgentID, GroupID, delegate
{
return c.GetGroupMembers(AgentUUIForOutside(RequestingAgentID), GroupID, accessToken);
});
}
}
return new List<GroupMembersData>();
}
public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason)
{
reason = string.Empty;
string url = string.Empty, gname = string.Empty;
if (IsLocal(groupID, out url, out gname))
return m_LocalGroupsConnector.AddGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers, out reason);
else
{
reason = "Operation not allowed outside this group's origin world.";
return false;
}
}
public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(groupID, out url, out gname))
return m_LocalGroupsConnector.UpdateGroupRole(AgentUUI(RequestingAgentID), groupID, roleID, name, description, title, powers);
else
{
return false;
}
}
public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(groupID, out url, out gname))
m_LocalGroupsConnector.RemoveGroupRole(AgentUUI(RequestingAgentID), groupID, roleID);
else
{
return;
}
}
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID groupID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(groupID, out url, out gname))
return m_LocalGroupsConnector.GetGroupRoles(AgentUUI(RequestingAgentID), groupID);
else if (!string.IsNullOrEmpty(url))
{
ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID);
string accessToken = string.Empty;
if (membership != null)
accessToken = membership.AccessToken;
else
return null;
GroupsServiceHGConnector c = GetConnector(url);
if (c != null)
{
return m_CacheWrapper.GetGroupRoles(RequestingAgentID, groupID, delegate
{
return c.GetGroupRoles(AgentUUIForOutside(RequestingAgentID), groupID, accessToken);
});
}
}
return new List<GroupRolesData>();
}
public List<GroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID groupID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(groupID, out url, out gname))
return m_LocalGroupsConnector.GetGroupRoleMembers(AgentUUI(RequestingAgentID), groupID);
else if (!string.IsNullOrEmpty(url))
{
ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(RequestingAgentID, RequestingAgentID, groupID);
string accessToken = string.Empty;
if (membership != null)
accessToken = membership.AccessToken;
else
return null;
GroupsServiceHGConnector c = GetConnector(url);
if (c != null)
{
return m_CacheWrapper.GetGroupRoleMembers(RequestingAgentID, groupID, delegate
{
return c.GetGroupRoleMembers(AgentUUIForOutside(RequestingAgentID), groupID, accessToken);
});
}
}
return new List<GroupRoleMembersData>();
}
public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason)
{
string url = string.Empty;
string name = string.Empty;
reason = string.Empty;
UUID uid = new UUID(AgentID);
if (IsLocal(GroupID, out url, out name))
{
if (m_UserManagement.IsLocalGridUser(uid)) // local user
{
// normal case: local group, local user
return m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason);
}
else // local group, foreign user
{
// the user is accepting the invitation, or joining, where the group resides
token = UUID.Random().ToString();
bool success = m_LocalGroupsConnector.AddAgentToGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID, token, out reason);
if (success)
{
// Here we always return true. The user has been added to the local group,
// independent of whether the remote operation succeeds or not
url = m_UserManagement.GetUserServerURL(uid, "GroupsServerURI");
if (url == string.Empty)
{
reason = "You don't have an accessible groups server in your home world. You membership to this group in only within this grid.";
return true;
}
GroupsServiceHGConnector c = GetConnector(url);
if (c != null)
c.CreateProxy(AgentUUI(RequestingAgentID), AgentID, token, GroupID, m_LocalGroupsServiceLocation, name, out reason);
return true;
}
return false;
}
}
else if (m_UserManagement.IsLocalGridUser(uid)) // local user
{
// foreign group, local user. She's been added already by the HG service.
// Let's just check
if (m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID) != null)
return true;
}
reason = "Operation not allowed outside this group's origin world";
return false;
}
public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
string url = string.Empty, name = string.Empty;
if (!IsLocal(GroupID, out url, out name) && url != string.Empty)
{
ExtendedGroupMembershipData membership = m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
if (membership != null)
{
GroupsServiceHGConnector c = GetConnector(url);
if (c != null)
c.RemoveAgentFromGroup(AgentUUIForOutside(AgentID), GroupID, membership.AccessToken);
}
}
// remove from local service
m_LocalGroupsConnector.RemoveAgentFromGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
}
public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(groupID, out url, out gname))
return m_LocalGroupsConnector.AddAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID, groupID, roleID, AgentUUI(agentID));
else
return false;
}
public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
return m_LocalGroupsConnector.GetAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID); ;
}
public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
m_LocalGroupsConnector.RemoveAgentToGroupInvite(AgentUUI(RequestingAgentID), inviteID);
}
public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(GroupID, out url, out gname))
m_LocalGroupsConnector.AddAgentToGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID);
}
public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(GroupID, out url, out gname))
m_LocalGroupsConnector.RemoveAgentFromGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID);
}
public List<GroupRolesData> GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(GroupID, out url, out gname))
return m_LocalGroupsConnector.GetAgentGroupRoles(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
else
return new List<GroupRolesData>();
}
public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(GroupID, out url, out gname))
m_LocalGroupsConnector.SetAgentActiveGroup(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
}
public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID)
{
return m_LocalGroupsConnector.GetAgentActiveMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID));
}
public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(GroupID, out url, out gname))
m_LocalGroupsConnector.SetAgentActiveGroupRole(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, RoleID);
}
public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile)
{
m_LocalGroupsConnector.UpdateMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID, AcceptNotices, ListInProfile);
}
public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(GroupID, out url, out gname))
return m_LocalGroupsConnector.GetAgentGroupMembership(AgentUUI(RequestingAgentID), AgentUUI(AgentID), GroupID);
else
return null;
}
public List<GroupMembershipData> GetAgentGroupMemberships(string RequestingAgentID, string AgentID)
{
return m_LocalGroupsConnector.GetAgentGroupMemberships(AgentUUI(RequestingAgentID), AgentUUI(AgentID));
}
public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
{
string url = string.Empty, gname = string.Empty;
if (IsLocal(groupID, out url, out gname))
{
if (m_LocalGroupsConnector.AddGroupNotice(AgentUUI(RequestingAgentID), groupID, noticeID, fromName, subject, message,
hasAttachment, attType, attName, attItemID, AgentUUI(attOwnerID)))
{
// then send the notice to every grid for which there are members in this group
List<GroupMembersData> members = m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), groupID);
List<string> urls = new List<string>();
foreach (GroupMembersData m in members)
{
if (!m_UserManagement.IsLocalGridUser(m.AgentID))
{
string gURL = m_UserManagement.GetUserServerURL(m.AgentID, "GroupsServerURI");
if (!urls.Contains(gURL))
urls.Add(gURL);
}
}
// so we have the list of urls to send the notice to
// this may take a long time...
WorkManager.RunInThread(delegate
{
foreach (string u in urls)
{
GroupsServiceHGConnector c = GetConnector(u);
if (c != null)
{
c.AddNotice(AgentUUIForOutside(RequestingAgentID), groupID, noticeID, fromName, subject, message,
hasAttachment, attType, attName, attItemID, AgentUUIForOutside(attOwnerID));
}
}
}, null, string.Format("AddGroupNotice (agent {0}, group {1})", RequestingAgentID, groupID));
return true;
}
return false;
}
else
return false;
}
public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID)
{
GroupNoticeInfo notice = m_LocalGroupsConnector.GetGroupNotice(AgentUUI(RequestingAgentID), noticeID);
if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null)
ImportForeigner(notice.noticeData.AttachmentOwnerID);
return notice;
}
public List<ExtendedGroupNoticeData> GetGroupNotices(string RequestingAgentID, UUID GroupID)
{
return m_LocalGroupsConnector.GetGroupNotices(AgentUUI(RequestingAgentID), GroupID);
}
#endregion
#region hypergrid groups
private string AgentUUI(string AgentIDStr)
{
UUID AgentID = UUID.Zero;
if (!UUID.TryParse(AgentIDStr, out AgentID) || AgentID == UUID.Zero)
return UUID.Zero.ToString();
if (m_UserManagement.IsLocalGridUser(AgentID))
return AgentID.ToString();
AgentCircuitData agent = null;
foreach (Scene scene in m_Scenes)
{
agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID);
if (agent != null)
break;
}
if (agent != null)
return Util.ProduceUserUniversalIdentifier(agent);
// we don't know anything about this foreign user
// try asking the user management module, which may know more
return m_UserManagement.GetUserUUI(AgentID);
}
private string AgentUUIForOutside(string AgentIDStr)
{
UUID AgentID = UUID.Zero;
if (!UUID.TryParse(AgentIDStr, out AgentID) || AgentID == UUID.Zero)
return UUID.Zero.ToString();
AgentCircuitData agent = null;
foreach (Scene scene in m_Scenes)
{
agent = scene.AuthenticateHandler.GetAgentCircuitData(AgentID);
if (agent != null)
break;
}
if (agent == null) // oops
return AgentID.ToString();
return Util.ProduceUserUniversalIdentifier(agent);
}
private UUID ImportForeigner(string uID)
{
UUID userID = UUID.Zero;
string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
if (Util.ParseUniversalUserIdentifier(uID, out userID, out url, out first, out last, out tmp))
m_UserManagement.AddUser(userID, first, last, url);
return userID;
}
private bool IsLocal(UUID groupID, out string serviceLocation, out string name)
{
serviceLocation = string.Empty;
name = string.Empty;
if (groupID.Equals(UUID.Zero))
return true;
ExtendedGroupRecord group = m_LocalGroupsConnector.GetGroupRecord(UUID.Zero.ToString(), groupID, string.Empty);
if (group == null)
{
//m_log.DebugFormat("[XXX]: IsLocal? group {0} not found -- no.", groupID);
return false;
}
serviceLocation = group.ServiceLocation;
name = group.GroupName;
bool isLocal = (group.ServiceLocation == string.Empty);
//m_log.DebugFormat("[XXX]: IsLocal? {0}", isLocal);
return isLocal;
}
private GroupsServiceHGConnector GetConnector(string url)
{
lock (m_NetworkConnectors)
{
if (m_NetworkConnectors.ContainsKey(url))
return m_NetworkConnectors[url];
GroupsServiceHGConnector c = new GroupsServiceHGConnector(url);
m_NetworkConnectors[url] = c;
}
return m_NetworkConnectors[url];
}
#endregion
}
}

View File

@ -1,445 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Server.Handlers.Base;
using log4net;
using OpenMetaverse;
namespace OpenSim.Groups
{
public class HGGroupsServiceRobustConnector : ServiceConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private HGGroupsService m_GroupsService;
private string m_ConfigName = "Groups";
// Called by Robust shell
public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) :
this(config, server, configName, null, null)
{
}
// Called by the sim-bound module
public HGGroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName, IOfflineIMService im, IUserAccountService users) :
base(config, server, configName)
{
if (configName != String.Empty)
m_ConfigName = configName;
m_log.DebugFormat("[Groups.RobustHGConnector]: Starting with config name {0}", m_ConfigName);
string homeURI = Util.GetConfigVarFromSections<string>(config, "HomeURI",
new string[] { "Startup", "Hypergrid", m_ConfigName}, string.Empty);
if (homeURI == string.Empty)
throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide the HomeURI [Startup] or in section {0}", m_ConfigName));
IConfig cnf = config.Configs[m_ConfigName];
if (cnf == null)
throw new Exception(String.Format("[Groups.RobustHGConnector]: {0} section does not exist", m_ConfigName));
if (im == null)
{
string imDll = cnf.GetString("OfflineIMService", string.Empty);
if (imDll == string.Empty)
throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide OfflineIMService in section {0}", m_ConfigName));
Object[] args = new Object[] { config };
im = ServerUtils.LoadPlugin<IOfflineIMService>(imDll, args);
}
if (users == null)
{
string usersDll = cnf.GetString("UserAccountService", string.Empty);
if (usersDll == string.Empty)
throw new Exception(String.Format("[Groups.RobustHGConnector]: please provide UserAccountService in section {0}", m_ConfigName));
Object[] args = new Object[] { config };
users = ServerUtils.LoadPlugin<IUserAccountService>(usersDll, args);
}
m_GroupsService = new HGGroupsService(config, im, users, homeURI);
server.AddStreamHandler(new HGGroupsServicePostHandler(m_GroupsService));
}
}
public class HGGroupsServicePostHandler : BaseStreamHandler
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private HGGroupsService m_GroupsService;
public HGGroupsServicePostHandler(HGGroupsService service) :
base("POST", "/hg-groups")
{
m_GroupsService = service;
}
protected override byte[] ProcessRequest(string path, Stream requestData,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
string body;
using(StreamReader sr = new StreamReader(requestData))
body = sr.ReadToEnd();
body = body.Trim();
//m_log.DebugFormat("[XXX]: query String: {0}", body);
try
{
Dictionary<string, object> request =
ServerUtils.ParseQueryString(body);
if (!request.ContainsKey("METHOD"))
return FailureResult();
string method = request["METHOD"].ToString();
request.Remove("METHOD");
m_log.DebugFormat("[Groups.RobustHGConnector]: {0}", method);
switch (method)
{
case "POSTGROUP":
return HandleAddGroupProxy(request);
case "REMOVEAGENTFROMGROUP":
return HandleRemoveAgentFromGroup(request);
case "GETGROUP":
return HandleGetGroup(request);
case "ADDNOTICE":
return HandleAddNotice(request);
case "VERIFYNOTICE":
return HandleVerifyNotice(request);
case "GETGROUPMEMBERS":
return HandleGetGroupMembers(request);
case "GETGROUPROLES":
return HandleGetGroupRoles(request);
case "GETROLEMEMBERS":
return HandleGetRoleMembers(request);
}
m_log.DebugFormat("[Groups.RobustHGConnector]: unknown method request: {0}", method);
}
catch (Exception e)
{
m_log.Error(string.Format("[Groups.RobustHGConnector]: Exception {0} ", e.Message), e);
}
return FailureResult();
}
byte[] HandleAddGroupProxy(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID")
|| !request.ContainsKey("AgentID")
|| !request.ContainsKey("AccessToken") || !request.ContainsKey("Location"))
NullResult(result, "Bad network data");
else
{
string RequestingAgentID = request["RequestingAgentID"].ToString();
string agentID = request["AgentID"].ToString();
UUID groupID = new UUID(request["GroupID"].ToString());
string accessToken = request["AccessToken"].ToString();
string location = request["Location"].ToString();
string name = string.Empty;
if (request.ContainsKey("Name"))
name = request["Name"].ToString();
string reason = string.Empty;
bool success = m_GroupsService.CreateGroupProxy(RequestingAgentID, agentID, accessToken, groupID, location, name, out reason);
result["REASON"] = reason;
result["RESULT"] = success.ToString();
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleRemoveAgentFromGroup(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("AccessToken") || !request.ContainsKey("AgentID") ||
!request.ContainsKey("GroupID"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string agentID = request["AgentID"].ToString();
string token = request["AccessToken"].ToString();
if (!m_GroupsService.RemoveAgentFromGroup(agentID, agentID, groupID, token))
NullResult(result, "Internal error");
else
result["RESULT"] = "true";
}
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
byte[] HandleGetGroup(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AccessToken"))
NullResult(result, "Bad network data");
else
{
string RequestingAgentID = request["RequestingAgentID"].ToString();
string token = request["AccessToken"].ToString();
UUID groupID = UUID.Zero;
string groupName = string.Empty;
if (request.ContainsKey("GroupID"))
groupID = new UUID(request["GroupID"].ToString());
if (request.ContainsKey("Name"))
groupName = request["Name"].ToString();
ExtendedGroupRecord grec = m_GroupsService.GetGroupRecord(RequestingAgentID, groupID, groupName, token);
if (grec == null)
NullResult(result, "Group not found");
else
result["RESULT"] = GroupsDataUtils.GroupRecord(grec);
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetGroupMembers(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string requestingAgentID = request["RequestingAgentID"].ToString();
string token = request["AccessToken"].ToString();
List<ExtendedGroupMembersData> members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID, token);
if (members == null || (members != null && members.Count == 0))
{
NullResult(result, "No members");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (ExtendedGroupMembersData m in members)
{
dict["m-" + i++] = GroupsDataUtils.GroupMembersData(m);
}
result["RESULT"] = dict;
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetGroupRoles(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string requestingAgentID = request["RequestingAgentID"].ToString();
string token = request["AccessToken"].ToString();
List<GroupRolesData> roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID, token);
if (roles == null || (roles != null && roles.Count == 0))
{
NullResult(result, "No members");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (GroupRolesData r in roles)
dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r);
result["RESULT"] = dict;
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetRoleMembers(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AccessToken"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string requestingAgentID = request["RequestingAgentID"].ToString();
string token = request["AccessToken"].ToString();
List<ExtendedGroupRoleMembersData> rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID, token);
if (rmembers == null || (rmembers != null && rmembers.Count == 0))
{
NullResult(result, "No members");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (ExtendedGroupRoleMembersData rm in rmembers)
dict["rm-" + i++] = GroupsDataUtils.GroupRoleMembersData(rm);
result["RESULT"] = dict;
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleAddNotice(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("NoticeID") ||
!request.ContainsKey("FromName") || !request.ContainsKey("Subject") || !request.ContainsKey("Message") ||
!request.ContainsKey("HasAttachment"))
NullResult(result, "Bad network data");
else
{
bool hasAtt = bool.Parse(request["HasAttachment"].ToString());
byte attType = 0;
string attName = string.Empty;
string attOwner = string.Empty;
UUID attItem = UUID.Zero;
if (request.ContainsKey("AttachmentType"))
attType = byte.Parse(request["AttachmentType"].ToString());
if (request.ContainsKey("AttachmentName"))
attName = request["AttachmentType"].ToString();
if (request.ContainsKey("AttachmentItemID"))
attItem = new UUID(request["AttachmentItemID"].ToString());
if (request.ContainsKey("AttachmentOwnerID"))
attOwner = request["AttachmentOwnerID"].ToString();
bool success = m_GroupsService.AddNotice(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()),
new UUID(request["NoticeID"].ToString()), request["FromName"].ToString(), request["Subject"].ToString(),
request["Message"].ToString(), hasAtt, attType, attName, attItem, attOwner);
result["RESULT"] = success.ToString();
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleVerifyNotice(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("NoticeID") || !request.ContainsKey("GroupID"))
NullResult(result, "Bad network data");
else
{
UUID noticeID = new UUID(request["NoticeID"].ToString());
UUID groupID = new UUID(request["GroupID"].ToString());
bool success = m_GroupsService.VerifyNotice(noticeID, groupID);
//m_log.DebugFormat("[XXX]: VerifyNotice returned {0}", success);
result["RESULT"] = success.ToString();
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
//
//
//
//
//
#region Helpers
private void NullResult(Dictionary<string, object> result, string reason)
{
result["RESULT"] = "NULL";
result["REASON"] = reason;
}
private byte[] FailureResult()
{
Dictionary<string, object> result = new Dictionary<string, object>();
NullResult(result, "Unknown method");
string xmlString = ServerUtils.BuildXmlResponse(result);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
#endregion
}
}

View File

@ -1,112 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using OpenMetaverse;
using OpenSim.Framework;
namespace OpenSim.Groups
{
public interface IGroupsServicesConnector
{
UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee,
bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID, out string reason);
bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee,
bool openEnrollment, bool allowPublish, bool maturePublish, out string reason);
ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName);
List<DirGroupsReplyData> FindGroups(string RequestingAgentIDstr, string search);
List<GroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID);
bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason);
bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers);
void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID);
List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID);
List<GroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID);
bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason);
void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID);
bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID);
GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID);
void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID);
void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID);
void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID);
List<GroupRolesData> GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID);
void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID);
ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID);
void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID);
void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile);
/// <summary>
/// Get information about a specific group to which the user belongs.
/// </summary>
/// <param name="RequestingAgentID">The agent requesting the information.</param>
/// <param name="AgentID">The agent requested.</param>
/// <param name="GroupID">The group requested.</param>
/// <returns>
/// If the user is a member of the group then the data structure is returned. If not, then null is returned.
/// </returns>
ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID);
/// <summary>
/// Get information about the groups to which a user belongs.
/// </summary>
/// <param name="RequestingAgentID">The agent requesting the information.</param>
/// <param name="AgentID">The agent requested.</param>
/// <returns>
/// Information about the groups to which the user belongs. If the user belongs to no groups then an empty
/// list is returned.
/// </returns>
List<GroupMembershipData> GetAgentGroupMemberships(string RequestingAgentID, string AgentID);
bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID);
GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID);
List<ExtendedGroupNoticeData> GetGroupNotices(string RequestingAgentID, UUID GroupID);
}
public class GroupInviteInfo
{
public UUID GroupID = UUID.Zero;
public UUID RoleID = UUID.Zero;
public string AgentID = string.Empty;
public UUID InviteID = UUID.Zero;
}
public class GroupNoticeInfo
{
public ExtendedGroupNoticeData noticeData = new ExtendedGroupNoticeData();
public UUID GroupID = UUID.Zero;
public string Message = string.Empty;
}
}

View File

@ -1,326 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
using OpenMetaverse;
using Mono.Addins;
using log4net;
using Nini.Config;
namespace OpenSim.Groups
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceLocalConnectorModule")]
public class GroupsServiceLocalConnectorModule : ISharedRegionModule, IGroupsServicesConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private bool m_Enabled = false;
private GroupsService m_GroupsService;
private IUserManagement m_UserManagement;
private List<Scene> m_Scenes;
private ForeignImporter m_ForeignImporter;
#region constructors
public GroupsServiceLocalConnectorModule()
{
}
public GroupsServiceLocalConnectorModule(IConfigSource config, IUserManagement uman)
{
Init(config);
m_UserManagement = uman;
m_ForeignImporter = new ForeignImporter(uman);
}
#endregion
private void Init(IConfigSource config)
{
m_GroupsService = new GroupsService(config);
m_Scenes = new List<Scene>();
}
#region ISharedRegionModule
public void Initialise(IConfigSource config)
{
IConfig groupsConfig = config.Configs["Groups"];
if (groupsConfig == null)
return;
if ((groupsConfig.GetBoolean("Enabled", false) == false)
|| (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name))
{
return;
}
Init(config);
m_Enabled = true;
m_log.DebugFormat("[Groups]: Initializing {0}", this.Name);
}
public string Name
{
get { return "Groups Local Service Connector"; }
}
public Type ReplaceableInterface
{
get { return null; }
}
public void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
m_log.DebugFormat("[Groups]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName);
scene.RegisterModuleInterface<IGroupsServicesConnector>(this);
m_Scenes.Add(scene);
}
public void RemoveRegion(Scene scene)
{
if (!m_Enabled)
return;
scene.UnregisterModuleInterface<IGroupsServicesConnector>(this);
m_Scenes.Remove(scene);
}
public void RegionLoaded(Scene scene)
{
if (!m_Enabled)
return;
if (m_UserManagement == null)
{
m_UserManagement = scene.RequestModuleInterface<IUserManagement>();
m_ForeignImporter = new ForeignImporter(m_UserManagement);
}
}
public void PostInitialise()
{
}
public void Close()
{
}
#endregion
#region IGroupsServicesConnector
public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment,
bool allowPublish, bool maturePublish, UUID founderID, out string reason)
{
m_log.DebugFormat("[Groups]: Creating group {0}", name);
reason = string.Empty;
return m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID,
membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason);
}
public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee,
bool openEnrollment, bool allowPublish, bool maturePublish, out string reason)
{
reason = string.Empty;
m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
return true;
}
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName)
{
if (GroupID != UUID.Zero)
return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID);
else if (GroupName != null)
return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupName);
return null;
}
public List<DirGroupsReplyData> FindGroups(string RequestingAgentIDstr, string search)
{
return m_GroupsService.FindGroups(RequestingAgentIDstr, search);
}
public List<GroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID)
{
List<ExtendedGroupMembersData> _members = m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID);
if (_members != null && _members.Count > 0)
{
List<GroupMembersData> members = _members.ConvertAll<GroupMembersData>(new Converter<ExtendedGroupMembersData, GroupMembersData>(m_ForeignImporter.ConvertGroupMembersData));
return members;
}
return new List<GroupMembersData>();
}
public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason)
{
return m_GroupsService.AddGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, out reason);
}
public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers)
{
return m_GroupsService.UpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers);
}
public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID)
{
m_GroupsService.RemoveGroupRole(RequestingAgentID, groupID, roleID);
}
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID)
{
return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID);
}
public List<GroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID)
{
List<ExtendedGroupRoleMembersData> _rm = m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID);
if (_rm != null && _rm.Count > 0)
{
List<GroupRoleMembersData> rm = _rm.ConvertAll<GroupRoleMembersData>(new Converter<ExtendedGroupRoleMembersData, GroupRoleMembersData>(m_ForeignImporter.ConvertGroupRoleMembersData));
return rm;
}
return new List<GroupRoleMembersData>();
}
public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason)
{
return m_GroupsService.AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, token, out reason);
}
public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
m_GroupsService.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
}
public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID)
{
return m_GroupsService.AddAgentToGroupInvite(RequestingAgentID, inviteID, groupID, roleID, agentID);
}
public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
return m_GroupsService.GetAgentToGroupInvite(RequestingAgentID, inviteID); ;
}
public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
m_GroupsService.RemoveAgentToGroupInvite(RequestingAgentID, inviteID);
}
public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
m_GroupsService.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
}
public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
m_GroupsService.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
}
public List<GroupRolesData> GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID)
{
return m_GroupsService.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID);
}
public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
m_GroupsService.SetAgentActiveGroup(RequestingAgentID, AgentID, GroupID);
}
public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID)
{
return m_GroupsService.GetAgentActiveMembership(RequestingAgentID, AgentID);
}
public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
m_GroupsService.SetAgentActiveGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
}
public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile)
{
m_GroupsService.UpdateMembership(RequestingAgentID, AgentID, GroupID, AcceptNotices, ListInProfile);
}
public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID)
{
return m_GroupsService.GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID); ;
}
public List<GroupMembershipData> GetAgentGroupMemberships(string RequestingAgentID, string AgentID)
{
return m_GroupsService.GetAgentGroupMemberships(RequestingAgentID, AgentID);
}
public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
{
return m_GroupsService.AddGroupNotice(RequestingAgentID, groupID, noticeID, fromName, subject, message,
hasAttachment, attType, attName, attItemID, attOwnerID);
}
public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID)
{
GroupNoticeInfo notice = m_GroupsService.GetGroupNotice(RequestingAgentID, noticeID);
//if (notice != null && notice.noticeData.HasAttachment && notice.noticeData.AttachmentOwnerID != null)
//{
// UUID userID = UUID.Zero;
// string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
// Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out userID, out url, out first, out last, out tmp);
// if (url != string.Empty)
// m_UserManagement.AddUser(userID, first, last, url);
//}
return notice;
}
public List<ExtendedGroupNoticeData> GetGroupNotices(string RequestingAgentID, UUID GroupID)
{
return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID);
}
#endregion
}
}

View File

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Mono.Addins;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenSim.Addons.Groups")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("http://opensimulator.org")]
[assembly: AssemblyProduct("OpenSim.Addons.Groups")]
[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("313d4865-d179-4735-9b5a-fe74885878b2")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]
[assembly: Addin("OpenSim.Groups", OpenSim.VersionInfo.VersionNumber)]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]

View File

@ -1,696 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using OpenSim.Framework;
using OpenSim.Framework.ServiceAuth;
using OpenSim.Server.Base;
using OpenMetaverse;
using log4net;
using Nini.Config;
namespace OpenSim.Groups
{
public class GroupsServiceRemoteConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private string m_ServerURI;
private IServiceAuth m_Auth;
private object m_Lock = new object();
public GroupsServiceRemoteConnector(IConfigSource config)
{
IConfig groupsConfig = config.Configs["Groups"];
string url = groupsConfig.GetString("GroupsServerURI", string.Empty);
if (!Uri.IsWellFormedUriString(url, UriKind.Absolute))
throw new Exception(string.Format("[Groups.RemoteConnector]: Malformed groups server URL {0}. Fix it or disable the Groups feature.", url));
m_ServerURI = url;
if (!m_ServerURI.EndsWith("/"))
m_ServerURI += "/";
/// This is from BaseServiceConnector
string authType = Util.GetConfigVarFromSections<string>(config, "AuthType", new string[] { "Network", "Groups" }, "None");
switch (authType)
{
case "BasicHttpAuthentication":
m_Auth = new BasicHttpAuthentication(config, "Groups");
break;
}
///
m_log.DebugFormat("[Groups.RemoteConnector]: Groups server at {0}, authentication {1}",
m_ServerURI, (m_Auth == null ? "None" : m_Auth.GetType().ToString()));
}
public ExtendedGroupRecord CreateGroup(string RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment,
bool allowPublish, bool maturePublish, UUID founderID, out string reason)
{
reason = string.Empty;
ExtendedGroupRecord rec = new ExtendedGroupRecord();
rec.AllowPublish = allowPublish;
rec.Charter = charter;
rec.FounderID = founderID;
rec.GroupName = name;
rec.GroupPicture = insigniaID;
rec.MaturePublish = maturePublish;
rec.MembershipFee = membershipFee;
rec.OpenEnrollment = openEnrollment;
rec.ShowInList = showInList;
Dictionary<string, object> sendData = GroupsDataUtils.GroupRecord(rec);
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "ADD";
Dictionary<string, object> ret = MakeRequest("PUTGROUP", sendData);
if (ret == null)
return null;
if (ret["RESULT"].ToString() == "NULL")
{
reason = ret["REASON"].ToString();
return null;
}
return GroupsDataUtils.GroupRecord((Dictionary<string, object>)ret["RESULT"]);
}
public ExtendedGroupRecord UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish)
{
ExtendedGroupRecord rec = new ExtendedGroupRecord();
rec.AllowPublish = allowPublish;
rec.Charter = charter;
rec.GroupPicture = insigniaID;
rec.MaturePublish = maturePublish;
rec.GroupID = groupID;
rec.MembershipFee = membershipFee;
rec.OpenEnrollment = openEnrollment;
rec.ShowInList = showInList;
Dictionary<string, object> sendData = GroupsDataUtils.GroupRecord(rec);
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "UPDATE";
Dictionary<string, object> ret = MakeRequest("PUTGROUP", sendData);
if (ret == null || (ret != null && (!ret.ContainsKey("RESULT") || ret["RESULT"].ToString() == "NULL")))
return null;
return GroupsDataUtils.GroupRecord((Dictionary<string, object>)ret["RESULT"]);
}
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName)
{
if (GroupID == UUID.Zero && (GroupName == null || (GroupName != null && GroupName == string.Empty)))
return null;
Dictionary<string, object> sendData = new Dictionary<string, object>();
if (GroupID != UUID.Zero)
sendData["GroupID"] = GroupID.ToString();
if (!string.IsNullOrEmpty(GroupName))
sendData["Name"] = GroupsDataUtils.Sanitize(GroupName);
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETGROUP", sendData);
if (ret == null || (ret != null && (!ret.ContainsKey("RESULT") || ret["RESULT"].ToString() == "NULL")))
return null;
return GroupsDataUtils.GroupRecord((Dictionary<string, object>)ret["RESULT"]);
}
public List<DirGroupsReplyData> FindGroups(string RequestingAgentIDstr, string query)
{
List<DirGroupsReplyData> hits = new List<DirGroupsReplyData>();
if (string.IsNullOrEmpty(query))
return hits;
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["Query"] = query;
sendData["RequestingAgentID"] = RequestingAgentIDstr;
Dictionary<string, object> ret = MakeRequest("FINDGROUPS", sendData);
if (ret == null)
return hits;
if (!ret.ContainsKey("RESULT"))
return hits;
if (ret["RESULT"].ToString() == "NULL")
return hits;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
DirGroupsReplyData m = GroupsDataUtils.DirGroupsReplyData((Dictionary<string, object>)v);
hits.Add(m);
}
return hits;
}
public GroupMembershipData AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason)
{
reason = string.Empty;
Dictionary<string, object> sendData = new Dictionary<string,object>();
sendData["AgentID"] = AgentID;
sendData["GroupID"] = GroupID.ToString();
sendData["RoleID"] = RoleID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["AccessToken"] = token;
Dictionary<string, object> ret = MakeRequest("ADDAGENTTOGROUP", sendData);
if (ret == null)
return null;
if (!ret.ContainsKey("RESULT"))
return null;
if (ret["RESULT"].ToString() == "NULL")
{
reason = ret["REASON"].ToString();
return null;
}
return GroupsDataUtils.GroupMembershipData((Dictionary<string, object>)ret["RESULT"]);
}
public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID;
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
MakeRequest("REMOVEAGENTFROMGROUP", sendData);
}
public ExtendedGroupMembershipData GetMembership(string RequestingAgentID, string AgentID, UUID GroupID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID;
if (GroupID != UUID.Zero)
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETMEMBERSHIP", sendData);
if (ret == null)
return null;
if (!ret.ContainsKey("RESULT"))
return null;
if (ret["RESULT"].ToString() == "NULL")
return null;
return GroupsDataUtils.GroupMembershipData((Dictionary<string, object>)ret["RESULT"]);
}
public List<GroupMembershipData> GetMemberships(string RequestingAgentID, string AgentID)
{
List<GroupMembershipData> memberships = new List<GroupMembershipData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID;
sendData["ALL"] = "true";
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETMEMBERSHIP", sendData);
if (ret == null)
return memberships;
if (!ret.ContainsKey("RESULT"))
return memberships;
if (ret["RESULT"].ToString() == "NULL")
return memberships;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
GroupMembershipData m = GroupsDataUtils.GroupMembershipData((Dictionary<string, object>)v);
memberships.Add(m);
}
return memberships;
}
public List<ExtendedGroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID)
{
List<ExtendedGroupMembersData> members = new List<ExtendedGroupMembersData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETGROUPMEMBERS", sendData);
if (ret == null)
return members;
if (!ret.ContainsKey("RESULT"))
return members;
if (ret["RESULT"].ToString() == "NULL")
return members;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary<string, object>)v);
members.Add(m);
}
return members;
}
public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason)
{
reason = string.Empty;
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = groupID.ToString();
sendData["RoleID"] = roleID.ToString();
sendData["Name"] = GroupsDataUtils.Sanitize(name);
sendData["Description"] = GroupsDataUtils.Sanitize(description);
sendData["Title"] = GroupsDataUtils.Sanitize(title);
sendData["Powers"] = powers.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "ADD";
Dictionary<string, object> ret = MakeRequest("PUTROLE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
{
reason = ret["REASON"].ToString();
return false;
}
return true;
}
public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = groupID.ToString();
sendData["RoleID"] = roleID.ToString();
sendData["Name"] = GroupsDataUtils.Sanitize(name);
sendData["Description"] = GroupsDataUtils.Sanitize(description);
sendData["Title"] = GroupsDataUtils.Sanitize(title);
sendData["Powers"] = powers.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "UPDATE";
Dictionary<string, object> ret = MakeRequest("PUTROLE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
return false;
return true;
}
public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = groupID.ToString();
sendData["RoleID"] = roleID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
MakeRequest("REMOVEROLE", sendData);
}
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID)
{
List<GroupRolesData> roles = new List<GroupRolesData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETGROUPROLES", sendData);
if (ret == null)
return roles;
if (!ret.ContainsKey("RESULT"))
return roles;
if (ret["RESULT"].ToString() == "NULL")
return roles;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary<string, object>)v);
roles.Add(m);
}
return roles;
}
public List<ExtendedGroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID)
{
List<ExtendedGroupRoleMembersData> rmembers = new List<ExtendedGroupRoleMembersData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETROLEMEMBERS", sendData);
if (ret == null)
return rmembers;
if (!ret.ContainsKey("RESULT"))
return rmembers;
if (ret["RESULT"].ToString() == "NULL")
return rmembers;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary<string, object>)v);
rmembers.Add(m);
}
return rmembers;
}
public bool AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID.ToString();
sendData["GroupID"] = GroupID.ToString();
sendData["RoleID"] = RoleID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "ADD";
Dictionary<string, object> ret = MakeRequest("AGENTROLE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
return false;
return true;
}
public bool RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID.ToString();
sendData["GroupID"] = GroupID.ToString();
sendData["RoleID"] = RoleID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "DELETE";
Dictionary<string, object> ret = MakeRequest("AGENTROLE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
return false;
return true;
}
public List<GroupRolesData> GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID)
{
List<GroupRolesData> roles = new List<GroupRolesData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID.ToString();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETAGENTROLES", sendData);
if (ret == null)
return roles;
if (!ret.ContainsKey("RESULT"))
return roles;
if (ret["RESULT"].ToString() == "NULL")
return roles;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary<string, object>)v);
roles.Add(m);
}
return roles;
}
public GroupMembershipData SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID.ToString();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "GROUP";
Dictionary<string, object> ret = MakeRequest("SETACTIVE", sendData);
if (ret == null)
return null;
if (!ret.ContainsKey("RESULT"))
return null;
if (ret["RESULT"].ToString() == "NULL")
return null;
return GroupsDataUtils.GroupMembershipData((Dictionary<string, object>)ret["RESULT"]);
}
public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID.ToString();
sendData["GroupID"] = GroupID.ToString();
sendData["RoleID"] = RoleID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "ROLE";
MakeRequest("SETACTIVE", sendData);
}
public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["AgentID"] = AgentID.ToString();
sendData["GroupID"] = GroupID.ToString();
sendData["AcceptNotices"] = AcceptNotices.ToString();
sendData["ListInProfile"] = ListInProfile.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
MakeRequest("UPDATEMEMBERSHIP", sendData);
}
public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["InviteID"] = inviteID.ToString();
sendData["GroupID"] = groupID.ToString();
sendData["RoleID"] = roleID.ToString();
sendData["AgentID"] = agentID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "ADD";
Dictionary<string, object> ret = MakeRequest("INVITE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true") // it may return "NULL"
return false;
return true;
}
public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["InviteID"] = inviteID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "GET";
Dictionary<string, object> ret = MakeRequest("INVITE", sendData);
if (ret == null)
return null;
if (!ret.ContainsKey("RESULT"))
return null;
if (ret["RESULT"].ToString() == "NULL")
return null;
return GroupsDataUtils.GroupInviteInfo((Dictionary<string, object>)ret["RESULT"]);
}
public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["InviteID"] = inviteID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
sendData["OP"] = "DELETE";
MakeRequest("INVITE", sendData);
}
public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = groupID.ToString();
sendData["NoticeID"] = noticeID.ToString();
sendData["FromName"] = GroupsDataUtils.Sanitize(fromName);
sendData["Subject"] = GroupsDataUtils.Sanitize(subject);
sendData["Message"] = GroupsDataUtils.Sanitize(message);
sendData["HasAttachment"] = hasAttachment.ToString();
if (hasAttachment)
{
sendData["AttachmentType"] = attType.ToString();
sendData["AttachmentName"] = attName.ToString();
sendData["AttachmentItemID"] = attItemID.ToString();
sendData["AttachmentOwnerID"] = attOwnerID;
}
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("ADDNOTICE", sendData);
if (ret == null)
return false;
if (!ret.ContainsKey("RESULT"))
return false;
if (ret["RESULT"].ToString().ToLower() != "true")
return false;
return true;
}
public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["NoticeID"] = noticeID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETNOTICES", sendData);
if (ret == null)
return null;
if (!ret.ContainsKey("RESULT"))
return null;
if (ret["RESULT"].ToString() == "NULL")
return null;
return GroupsDataUtils.GroupNoticeInfo((Dictionary<string, object>)ret["RESULT"]);
}
public List<ExtendedGroupNoticeData> GetGroupNotices(string RequestingAgentID, UUID GroupID)
{
List<ExtendedGroupNoticeData> notices = new List<ExtendedGroupNoticeData>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["GroupID"] = GroupID.ToString();
sendData["RequestingAgentID"] = RequestingAgentID;
Dictionary<string, object> ret = MakeRequest("GETNOTICES", sendData);
if (ret == null)
return notices;
if (!ret.ContainsKey("RESULT"))
return notices;
if (ret["RESULT"].ToString() == "NULL")
return notices;
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
ExtendedGroupNoticeData m = GroupsDataUtils.GroupNoticeData((Dictionary<string, object>)v);
notices.Add(m);
}
return notices;
}
#region Make Request
private Dictionary<string, object> MakeRequest(string method, Dictionary<string, object> sendData)
{
sendData["METHOD"] = method;
string reply = string.Empty;
lock (m_Lock)
reply = SynchronousRestFormsRequester.MakeRequest("POST",
m_ServerURI + "groups",
ServerUtils.BuildQueryString(sendData),
m_Auth);
if (reply == string.Empty)
return null;
Dictionary<string, object> replyData = ServerUtils.ParseXmlResponse(
reply);
return replyData;
}
#endregion
}
}

View File

@ -1,408 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Text;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using OpenMetaverse;
using Mono.Addins;
using log4net;
using Nini.Config;
namespace OpenSim.Groups
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsServiceRemoteConnectorModule")]
public class GroupsServiceRemoteConnectorModule : ISharedRegionModule, IGroupsServicesConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private bool m_Enabled = false;
private GroupsServiceRemoteConnector m_GroupsService;
private IUserManagement m_UserManagement;
private List<Scene> m_Scenes;
private RemoteConnectorCacheWrapper m_CacheWrapper;
#region constructors
public GroupsServiceRemoteConnectorModule()
{
}
public GroupsServiceRemoteConnectorModule(IConfigSource config, IUserManagement uman)
{
Init(config);
m_UserManagement = uman;
m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement);
}
#endregion
private void Init(IConfigSource config)
{
m_GroupsService = new GroupsServiceRemoteConnector(config);
m_Scenes = new List<Scene>();
}
#region ISharedRegionModule
public void Initialise(IConfigSource config)
{
IConfig groupsConfig = config.Configs["Groups"];
if (groupsConfig == null)
return;
if ((groupsConfig.GetBoolean("Enabled", false) == false)
|| (groupsConfig.GetString("ServicesConnectorModule", string.Empty) != Name))
{
return;
}
Init(config);
m_Enabled = true;
m_log.DebugFormat("[Groups.RemoteConnector]: Initializing {0}", this.Name);
}
public string Name
{
get { return "Groups Remote Service Connector"; }
}
public Type ReplaceableInterface
{
get { return null; }
}
public void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
m_log.DebugFormat("[Groups.RemoteConnector]: Registering {0} with {1}", this.Name, scene.RegionInfo.RegionName);
scene.RegisterModuleInterface<IGroupsServicesConnector>(this);
m_Scenes.Add(scene);
}
public void RemoveRegion(Scene scene)
{
if (!m_Enabled)
return;
scene.UnregisterModuleInterface<IGroupsServicesConnector>(this);
m_Scenes.Remove(scene);
}
public void RegionLoaded(Scene scene)
{
if (!m_Enabled)
return;
if (m_UserManagement == null)
{
m_UserManagement = scene.RequestModuleInterface<IUserManagement>();
m_CacheWrapper = new RemoteConnectorCacheWrapper(m_UserManagement);
}
}
public void PostInitialise()
{
}
public void Close()
{
}
#endregion
#region IGroupsServicesConnector
public UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment,
bool allowPublish, bool maturePublish, UUID founderID, out string reason)
{
m_log.DebugFormat("[Groups.RemoteConnector]: Creating group {0}", name);
string r = string.Empty;
UUID groupID = m_CacheWrapper.CreateGroup(RequestingAgentID, delegate
{
return m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID,
membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out r);
});
reason = r;
return groupID;
}
public bool UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee,
bool openEnrollment, bool allowPublish, bool maturePublish, out string reason)
{
string r = string.Empty;
bool success = m_CacheWrapper.UpdateGroup(groupID, delegate
{
return m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
});
reason = r;
return success;
}
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName)
{
if (GroupID == UUID.Zero && (GroupName == null || GroupName != null && GroupName == string.Empty))
return null;
return m_CacheWrapper.GetGroupRecord(RequestingAgentID,GroupID,GroupName, delegate
{
return m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID, GroupName);
});
}
public List<DirGroupsReplyData> FindGroups(string RequestingAgentIDstr, string search)
{
// TODO!
return m_GroupsService.FindGroups(RequestingAgentIDstr, search);
}
public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason)
{
string agentFullID = AgentID;
m_log.DebugFormat("[Groups.RemoteConnector]: Add agent {0} to group {1}", agentFullID, GroupID);
string r = string.Empty;
bool success = m_CacheWrapper.AddAgentToGroup(RequestingAgentID, AgentID, GroupID, delegate
{
return m_GroupsService.AddAgentToGroup(RequestingAgentID, agentFullID, GroupID, RoleID, token, out r);
});
reason = r;
return success;
}
public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
m_CacheWrapper.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID, delegate
{
m_GroupsService.RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
});
}
public void SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID)
{
m_CacheWrapper.SetAgentActiveGroup(AgentID, delegate
{
return m_GroupsService.SetAgentActiveGroup(RequestingAgentID, AgentID, GroupID);
});
}
public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID)
{
return m_CacheWrapper.GetAgentActiveMembership(AgentID, delegate
{
return m_GroupsService.GetMembership(RequestingAgentID, AgentID, UUID.Zero);
});
}
public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID)
{
return m_CacheWrapper.GetAgentGroupMembership(AgentID, GroupID, delegate
{
return m_GroupsService.GetMembership(RequestingAgentID, AgentID, GroupID);
});
}
public List<GroupMembershipData> GetAgentGroupMemberships(string RequestingAgentID, string AgentID)
{
return m_CacheWrapper.GetAgentGroupMemberships(AgentID, delegate
{
return m_GroupsService.GetMemberships(RequestingAgentID, AgentID);
});
}
public List<GroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID)
{
return m_CacheWrapper.GetGroupMembers(RequestingAgentID, GroupID, delegate
{
return m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID);
});
}
public bool AddGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, out string reason)
{
string r = string.Empty;
bool success = m_CacheWrapper.AddGroupRole(groupID, roleID, description, name, powers, title, delegate
{
return m_GroupsService.AddGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, out r);
});
reason = r;
return success;
}
public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers)
{
return m_CacheWrapper.UpdateGroupRole(groupID, roleID, name, description, title, powers, delegate
{
return m_GroupsService.UpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers);
});
}
public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID)
{
m_CacheWrapper.RemoveGroupRole(RequestingAgentID, groupID, roleID, delegate
{
m_GroupsService.RemoveGroupRole(RequestingAgentID, groupID, roleID);
});
}
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID)
{
return m_CacheWrapper.GetGroupRoles(RequestingAgentID, GroupID, delegate
{
return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID);
});
}
public List<GroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID)
{
return m_CacheWrapper.GetGroupRoleMembers(RequestingAgentID, GroupID, delegate
{
return m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID);
});
}
public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
m_CacheWrapper.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID, delegate
{
return m_GroupsService.AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
});
}
public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
m_CacheWrapper.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID, delegate
{
return m_GroupsService.RemoveAgentFromGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
});
}
public List<GroupRolesData> GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID)
{
return m_CacheWrapper.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID, delegate
{
return m_GroupsService.GetAgentGroupRoles(RequestingAgentID, AgentID, GroupID); ;
});
}
public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID)
{
m_CacheWrapper.SetAgentActiveGroupRole(AgentID, GroupID, delegate
{
m_GroupsService.SetAgentActiveGroupRole(RequestingAgentID, AgentID, GroupID, RoleID);
});
}
public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile)
{
m_CacheWrapper.UpdateMembership(AgentID, GroupID, AcceptNotices, ListInProfile, delegate
{
m_GroupsService.UpdateMembership(RequestingAgentID, AgentID, GroupID, AcceptNotices, ListInProfile);
});
}
public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, string agentID)
{
return m_GroupsService.AddAgentToGroupInvite(RequestingAgentID, inviteID, groupID, roleID, agentID);
}
public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
return m_GroupsService.GetAgentToGroupInvite(RequestingAgentID, inviteID);
}
public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID)
{
m_GroupsService.RemoveAgentToGroupInvite(RequestingAgentID, inviteID);
}
public bool AddGroupNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
{
GroupNoticeInfo notice = new GroupNoticeInfo();
notice.GroupID = groupID;
notice.Message = message;
notice.noticeData = new ExtendedGroupNoticeData();
notice.noticeData.AttachmentItemID = attItemID;
notice.noticeData.AttachmentName = attName;
notice.noticeData.AttachmentOwnerID = attOwnerID.ToString();
notice.noticeData.AttachmentType = attType;
notice.noticeData.FromName = fromName;
notice.noticeData.HasAttachment = hasAttachment;
notice.noticeData.NoticeID = noticeID;
notice.noticeData.Subject = subject;
notice.noticeData.Timestamp = (uint)Util.UnixTimeSinceEpoch();
return m_CacheWrapper.AddGroupNotice(groupID, noticeID, notice, delegate
{
return m_GroupsService.AddGroupNotice(RequestingAgentID, groupID, noticeID, fromName, subject, message,
hasAttachment, attType, attName, attItemID, attOwnerID);
});
}
public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID)
{
return m_CacheWrapper.GetGroupNotice(noticeID, delegate
{
return m_GroupsService.GetGroupNotice(RequestingAgentID, noticeID);
});
}
public List<ExtendedGroupNoticeData> GetGroupNotices(string RequestingAgentID, UUID GroupID)
{
return m_CacheWrapper.GetGroupNotices(GroupID, delegate
{
return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID);
});
}
#endregion
}
}

View File

@ -1,817 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Framework.ServiceAuth;
using OpenSim.Server.Handlers.Base;
using log4net;
using OpenMetaverse;
namespace OpenSim.Groups
{
public class GroupsServiceRobustConnector : ServiceConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private GroupsService m_GroupsService;
private string m_ConfigName = "Groups";
public GroupsServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) :
base(config, server, configName)
{
string key = string.Empty;
if (configName != String.Empty)
m_ConfigName = configName;
m_log.DebugFormat("[Groups.RobustConnector]: Starting with config name {0}", m_ConfigName);
IConfig groupsConfig = config.Configs[m_ConfigName];
if (groupsConfig != null)
{
key = groupsConfig.GetString("SecretKey", string.Empty);
m_log.DebugFormat("[Groups.RobustConnector]: Starting with secret key {0}", key);
}
// else
// m_log.DebugFormat("[Groups.RobustConnector]: Unable to find {0} section in configuration", m_ConfigName);
m_GroupsService = new GroupsService(config);
IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName);
server.AddStreamHandler(new GroupsServicePostHandler(m_GroupsService, auth));
}
}
public class GroupsServicePostHandler : BaseStreamHandler
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private GroupsService m_GroupsService;
public GroupsServicePostHandler(GroupsService service, IServiceAuth auth) :
base("POST", "/groups", auth)
{
m_GroupsService = service;
}
protected override byte[] ProcessRequest(string path, Stream requestData,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
string body;
using(StreamReader sr = new StreamReader(requestData))
body = sr.ReadToEnd();
body = body.Trim();
//m_log.DebugFormat("[XXX]: query String: {0}", body);
try
{
Dictionary<string, object> request =
ServerUtils.ParseQueryString(body);
if (!request.ContainsKey("METHOD"))
return FailureResult();
string method = request["METHOD"].ToString();
request.Remove("METHOD");
// m_log.DebugFormat("[Groups.Handler]: {0}", method);
switch (method)
{
case "PUTGROUP":
return HandleAddOrUpdateGroup(request);
case "GETGROUP":
return HandleGetGroup(request);
case "ADDAGENTTOGROUP":
return HandleAddAgentToGroup(request);
case "REMOVEAGENTFROMGROUP":
return HandleRemoveAgentFromGroup(request);
case "GETMEMBERSHIP":
return HandleGetMembership(request);
case "GETGROUPMEMBERS":
return HandleGetGroupMembers(request);
case "PUTROLE":
return HandlePutRole(request);
case "REMOVEROLE":
return HandleRemoveRole(request);
case "GETGROUPROLES":
return HandleGetGroupRoles(request);
case "GETROLEMEMBERS":
return HandleGetRoleMembers(request);
case "AGENTROLE":
return HandleAgentRole(request);
case "GETAGENTROLES":
return HandleGetAgentRoles(request);
case "SETACTIVE":
return HandleSetActive(request);
case "UPDATEMEMBERSHIP":
return HandleUpdateMembership(request);
case "INVITE":
return HandleInvite(request);
case "ADDNOTICE":
return HandleAddNotice(request);
case "GETNOTICES":
return HandleGetNotices(request);
case "FINDGROUPS":
return HandleFindGroups(request);
}
m_log.DebugFormat("[GROUPS HANDLER]: unknown method request: {0}", method);
}
catch (Exception e)
{
m_log.Error(string.Format("[GROUPS HANDLER]: Exception {0} ", e.Message), e);
}
return FailureResult();
}
byte[] HandleAddOrUpdateGroup(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
ExtendedGroupRecord grec = GroupsDataUtils.GroupRecord(request);
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("OP"))
NullResult(result, "Bad network data");
else
{
string RequestingAgentID = request["RequestingAgentID"].ToString();
string reason = string.Empty;
string op = request["OP"].ToString();
if (op == "ADD")
{
grec.GroupID = m_GroupsService.CreateGroup(RequestingAgentID, grec.GroupName, grec.Charter, grec.ShowInList, grec.GroupPicture, grec.MembershipFee,
grec.OpenEnrollment, grec.AllowPublish, grec.MaturePublish, grec.FounderID, out reason);
}
else if (op == "UPDATE")
{
m_GroupsService.UpdateGroup(RequestingAgentID, grec.GroupID, grec.Charter, grec.ShowInList, grec.GroupPicture, grec.MembershipFee,
grec.OpenEnrollment, grec.AllowPublish, grec.MaturePublish);
}
if (grec.GroupID != UUID.Zero)
{
grec = m_GroupsService.GetGroupRecord(RequestingAgentID, grec.GroupID);
if (grec == null)
NullResult(result, "Internal Error");
else
result["RESULT"] = GroupsDataUtils.GroupRecord(grec);
}
else
NullResult(result, reason);
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetGroup(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID"))
NullResult(result, "Bad network data");
else
{
string RequestingAgentID = request["RequestingAgentID"].ToString();
ExtendedGroupRecord grec = null;
if (request.ContainsKey("GroupID"))
{
UUID groupID = new UUID(request["GroupID"].ToString());
grec = m_GroupsService.GetGroupRecord(RequestingAgentID, groupID);
}
else if (request.ContainsKey("Name"))
{
string name = request["Name"].ToString();
grec = m_GroupsService.GetGroupRecord(RequestingAgentID, name);
}
if (grec == null)
NullResult(result, "Group not found");
else
result["RESULT"] = GroupsDataUtils.GroupRecord(grec);
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleAddAgentToGroup(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") ||
!request.ContainsKey("GroupID") || !request.ContainsKey("RoleID"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
UUID roleID = new UUID(request["RoleID"].ToString());
string agentID = request["AgentID"].ToString();
string requestingAgentID = request["RequestingAgentID"].ToString();
string token = string.Empty;
string reason = string.Empty;
if (request.ContainsKey("AccessToken"))
token = request["AccessToken"].ToString();
if (!m_GroupsService.AddAgentToGroup(requestingAgentID, agentID, groupID, roleID, token, out reason))
NullResult(result, reason);
else
{
GroupMembershipData membership = m_GroupsService.GetAgentGroupMembership(requestingAgentID, agentID, groupID);
if (membership == null)
NullResult(result, "Internal error");
else
result["RESULT"] = GroupsDataUtils.GroupMembershipData((ExtendedGroupMembershipData)membership);
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleRemoveAgentFromGroup(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || !request.ContainsKey("GroupID"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string agentID = request["AgentID"].ToString();
string requestingAgentID = request["RequestingAgentID"].ToString();
if (!m_GroupsService.RemoveAgentFromGroup(requestingAgentID, agentID, groupID))
NullResult(result, string.Format("Insufficient permissions. {0}", agentID));
else
result["RESULT"] = "true";
}
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
byte[] HandleGetMembership(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID"))
NullResult(result, "Bad network data");
else
{
string agentID = request["AgentID"].ToString();
UUID groupID = UUID.Zero;
if (request.ContainsKey("GroupID"))
groupID = new UUID(request["GroupID"].ToString());
string requestingAgentID = request["RequestingAgentID"].ToString();
bool all = request.ContainsKey("ALL");
if (!all)
{
ExtendedGroupMembershipData membership = null;
if (groupID == UUID.Zero)
{
membership = m_GroupsService.GetAgentActiveMembership(requestingAgentID, agentID);
}
else
{
membership = m_GroupsService.GetAgentGroupMembership(requestingAgentID, agentID, groupID);
}
if (membership == null)
NullResult(result, "No such membership");
else
result["RESULT"] = GroupsDataUtils.GroupMembershipData(membership);
}
else
{
List<GroupMembershipData> memberships = m_GroupsService.GetAgentGroupMemberships(requestingAgentID, agentID);
if (memberships == null || (memberships != null && memberships.Count == 0))
{
NullResult(result, "No memberships");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (GroupMembershipData m in memberships)
dict["m-" + i++] = GroupsDataUtils.GroupMembershipData((ExtendedGroupMembershipData)m);
result["RESULT"] = dict;
}
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetGroupMembers(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string requestingAgentID = request["RequestingAgentID"].ToString();
List<ExtendedGroupMembersData> members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID);
if (members == null || (members != null && members.Count == 0))
{
NullResult(result, "No members");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (ExtendedGroupMembersData m in members)
{
dict["m-" + i++] = GroupsDataUtils.GroupMembersData(m);
}
result["RESULT"] = dict;
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandlePutRole(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID") ||
!request.ContainsKey("Name") || !request.ContainsKey("Description") || !request.ContainsKey("Title") ||
!request.ContainsKey("Powers") || !request.ContainsKey("OP"))
NullResult(result, "Bad network data");
else
{
string op = request["OP"].ToString();
string reason = string.Empty;
bool success = false;
if (op == "ADD")
success = m_GroupsService.AddGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()),
new UUID(request["RoleID"].ToString()), request["Name"].ToString(), request["Description"].ToString(),
request["Title"].ToString(), UInt64.Parse(request["Powers"].ToString()), out reason);
else if (op == "UPDATE")
success = m_GroupsService.UpdateGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()),
new UUID(request["RoleID"].ToString()), request["Name"].ToString(), request["Description"].ToString(),
request["Title"].ToString(), UInt64.Parse(request["Powers"].ToString()));
result["RESULT"] = success.ToString();
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleRemoveRole(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID"))
NullResult(result, "Bad network data");
else
{
m_GroupsService.RemoveGroupRole(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()),
new UUID(request["RoleID"].ToString()));
result["RESULT"] = "true";
}
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
byte[] HandleGetGroupRoles(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string requestingAgentID = request["RequestingAgentID"].ToString();
List<GroupRolesData> roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID);
if (roles == null || (roles != null && roles.Count == 0))
{
NullResult(result, "No members");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (GroupRolesData r in roles)
dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r);
result["RESULT"] = dict;
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetRoleMembers(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string requestingAgentID = request["RequestingAgentID"].ToString();
List<ExtendedGroupRoleMembersData> rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID);
if (rmembers == null || (rmembers != null && rmembers.Count == 0))
{
NullResult(result, "No members");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (ExtendedGroupRoleMembersData rm in rmembers)
dict["rm-" + i++] = GroupsDataUtils.GroupRoleMembersData(rm);
result["RESULT"] = dict;
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleAgentRole(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("RoleID") ||
!request.ContainsKey("AgentID") || !request.ContainsKey("OP"))
NullResult(result, "Bad network data");
else
{
string op = request["OP"].ToString();
bool success = false;
if (op == "ADD")
success = m_GroupsService.AddAgentToGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(),
new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString()));
else if (op == "DELETE")
success = m_GroupsService.RemoveAgentFromGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(),
new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString()));
result["RESULT"] = success.ToString();
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetAgentRoles(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("AgentID"))
NullResult(result, "Bad network data");
else
{
UUID groupID = new UUID(request["GroupID"].ToString());
string agentID = request["AgentID"].ToString();
string requestingAgentID = request["RequestingAgentID"].ToString();
List<GroupRolesData> roles = m_GroupsService.GetAgentGroupRoles(requestingAgentID, agentID, groupID);
if (roles == null || (roles != null && roles.Count == 0))
{
NullResult(result, "No members");
}
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (GroupRolesData r in roles)
dict["r-" + i++] = GroupsDataUtils.GroupRolesData(r);
result["RESULT"] = dict;
}
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleSetActive(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") ||
!request.ContainsKey("AgentID") || !request.ContainsKey("OP"))
{
NullResult(result, "Bad network data");
string xmlString = ServerUtils.BuildXmlResponse(result);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
else
{
string op = request["OP"].ToString();
if (op == "GROUP")
{
ExtendedGroupMembershipData group = m_GroupsService.SetAgentActiveGroup(request["RequestingAgentID"].ToString(),
request["AgentID"].ToString(), new UUID(request["GroupID"].ToString()));
if (group == null)
NullResult(result, "Internal error");
else
result["RESULT"] = GroupsDataUtils.GroupMembershipData(group);
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
else if (op == "ROLE" && request.ContainsKey("RoleID"))
{
m_GroupsService.SetAgentActiveGroupRole(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(),
new UUID(request["GroupID"].ToString()), new UUID(request["RoleID"].ToString()));
result["RESULT"] = "true";
}
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
}
byte[] HandleUpdateMembership(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("AgentID") || !request.ContainsKey("GroupID") ||
!request.ContainsKey("AcceptNotices") || !request.ContainsKey("ListInProfile"))
NullResult(result, "Bad network data");
else
{
m_GroupsService.UpdateMembership(request["RequestingAgentID"].ToString(), request["AgentID"].ToString(), new UUID(request["GroupID"].ToString()),
bool.Parse(request["AcceptNotices"].ToString()), bool.Parse(request["ListInProfile"].ToString()));
result["RESULT"] = "true";
}
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
byte[] HandleInvite(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("InviteID"))
{
NullResult(result, "Bad network data");
string xmlString = ServerUtils.BuildXmlResponse(result);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
else
{
string op = request["OP"].ToString();
if (op == "ADD" && request.ContainsKey("GroupID") && request.ContainsKey("RoleID") && request.ContainsKey("AgentID"))
{
bool success = m_GroupsService.AddAgentToGroupInvite(request["RequestingAgentID"].ToString(),
new UUID(request["InviteID"].ToString()), new UUID(request["GroupID"].ToString()),
new UUID(request["RoleID"].ToString()), request["AgentID"].ToString());
result["RESULT"] = success.ToString();
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
else if (op == "DELETE")
{
m_GroupsService.RemoveAgentToGroupInvite(request["RequestingAgentID"].ToString(), new UUID(request["InviteID"].ToString()));
result["RESULT"] = "true";
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
else if (op == "GET")
{
GroupInviteInfo invite = m_GroupsService.GetAgentToGroupInvite(request["RequestingAgentID"].ToString(),
new UUID(request["InviteID"].ToString()));
if (invite != null)
result["RESULT"] = GroupsDataUtils.GroupInviteInfo(invite);
else
result["RESULT"] = "NULL";
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
NullResult(result, "Bad OP in request");
return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result));
}
}
byte[] HandleAddNotice(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("GroupID") || !request.ContainsKey("NoticeID") ||
!request.ContainsKey("FromName") || !request.ContainsKey("Subject") || !request.ContainsKey("Message") ||
!request.ContainsKey("HasAttachment"))
NullResult(result, "Bad network data");
else
{
bool hasAtt = bool.Parse(request["HasAttachment"].ToString());
byte attType = 0;
string attName = string.Empty;
string attOwner = string.Empty;
UUID attItem = UUID.Zero;
if (request.ContainsKey("AttachmentType"))
attType = byte.Parse(request["AttachmentType"].ToString());
if (request.ContainsKey("AttachmentName"))
attName = request["AttachmentName"].ToString();
if (request.ContainsKey("AttachmentItemID"))
attItem = new UUID(request["AttachmentItemID"].ToString());
if (request.ContainsKey("AttachmentOwnerID"))
attOwner = request["AttachmentOwnerID"].ToString();
bool success = m_GroupsService.AddGroupNotice(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()),
new UUID(request["NoticeID"].ToString()), request["FromName"].ToString(), request["Subject"].ToString(),
request["Message"].ToString(), hasAtt, attType, attName, attItem, attOwner);
result["RESULT"] = success.ToString();
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGetNotices(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID"))
NullResult(result, "Bad network data");
else if (request.ContainsKey("NoticeID")) // just one
{
GroupNoticeInfo notice = m_GroupsService.GetGroupNotice(request["RequestingAgentID"].ToString(), new UUID(request["NoticeID"].ToString()));
if (notice == null)
NullResult(result, "NO such notice");
else
result["RESULT"] = GroupsDataUtils.GroupNoticeInfo(notice);
}
else if (request.ContainsKey("GroupID")) // all notices for group
{
List<ExtendedGroupNoticeData> notices = m_GroupsService.GetGroupNotices(request["RequestingAgentID"].ToString(), new UUID(request["GroupID"].ToString()));
if (notices == null || (notices != null && notices.Count == 0))
NullResult(result, "No notices");
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (ExtendedGroupNoticeData n in notices)
dict["n-" + i++] = GroupsDataUtils.GroupNoticeData(n);
result["RESULT"] = dict;
}
}
else
NullResult(result, "Bad OP in request");
string xmlString = ServerUtils.BuildXmlResponse(result);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleFindGroups(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("Query"))
NullResult(result, "Bad network data");
List<DirGroupsReplyData> hits = m_GroupsService.FindGroups(request["RequestingAgentID"].ToString(), request["Query"].ToString());
if (hits == null || (hits != null && hits.Count == 0))
NullResult(result, "No hits");
else
{
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (DirGroupsReplyData n in hits)
dict["n-" + i++] = GroupsDataUtils.DirGroupsReplyData(n);
result["RESULT"] = dict;
}
string xmlString = ServerUtils.BuildXmlResponse(result);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
#region Helpers
private void NullResult(Dictionary<string, object> result, string reason)
{
result["RESULT"] = "NULL";
result["REASON"] = reason;
}
private byte[] FailureResult()
{
Dictionary<string, object> result = new Dictionary<string, object>();
NullResult(result, "Unknown method");
string xmlString = ServerUtils.BuildXmlResponse(result);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
private byte[] FailureResult(string reason)
{
Dictionary<string, object> result = new Dictionary<string, object>();
NullResult(result, reason);
string xmlString = ServerUtils.BuildXmlResponse(result);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
#endregion
}
}

View File

@ -1,888 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using OpenSim.Framework;
//using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
using OpenMetaverse;
namespace OpenSim.Groups
{
public delegate ExtendedGroupRecord GroupRecordDelegate();
public delegate GroupMembershipData GroupMembershipDelegate();
public delegate List<GroupMembershipData> GroupMembershipListDelegate();
public delegate List<ExtendedGroupMembersData> GroupMembersListDelegate();
public delegate List<GroupRolesData> GroupRolesListDelegate();
public delegate List<ExtendedGroupRoleMembersData> RoleMembersListDelegate();
public delegate GroupNoticeInfo NoticeDelegate();
public delegate List<ExtendedGroupNoticeData> NoticeListDelegate();
public delegate void VoidDelegate();
public delegate bool BooleanDelegate();
public class RemoteConnectorCacheWrapper
{
private ForeignImporter m_ForeignImporter;
private Dictionary<string, bool> m_ActiveRequests = new Dictionary<string, bool>();
private const int GROUPS_CACHE_TIMEOUT = 1 * 60; // 1 minutes
// This all important cache cahces objects of different types:
// group-<GroupID> or group-<Name> => ExtendedGroupRecord
// active-<AgentID> => GroupMembershipData
// membership-<AgentID>-<GroupID> => GroupMembershipData
// memberships-<AgentID> => List<GroupMembershipData>
// members-<RequestingAgentID>-<GroupID> => List<ExtendedGroupMembersData>
// role-<RoleID> => GroupRolesData
// roles-<GroupID> => List<GroupRolesData> ; all roles in the group
// roles-<GroupID>-<AgentID> => List<GroupRolesData> ; roles that the agent has
// rolemembers-<RequestingAgentID>-<GroupID> => List<ExtendedGroupRoleMembersData>
// notice-<noticeID> => GroupNoticeInfo
// notices-<GroupID> => List<ExtendedGroupNoticeData>
private ExpiringCache<string, object> m_Cache = new ExpiringCache<string, object>();
public RemoteConnectorCacheWrapper(IUserManagement uman)
{
m_ForeignImporter = new ForeignImporter(uman);
}
public UUID CreateGroup(UUID RequestingAgentID, GroupRecordDelegate d)
{
//m_log.DebugFormat("[Groups.RemoteConnector]: Creating group {0}", name);
//reason = string.Empty;
//ExtendedGroupRecord group = m_GroupsService.CreateGroup(RequestingAgentID.ToString(), name, charter, showInList, insigniaID,
// membershipFee, openEnrollment, allowPublish, maturePublish, founderID, out reason);
ExtendedGroupRecord group = d();
if (group == null)
return UUID.Zero;
if (group.GroupID != UUID.Zero)
lock (m_Cache)
{
m_Cache.Add("group-" + group.GroupID.ToString(), group, GROUPS_CACHE_TIMEOUT);
if (m_Cache.Contains("memberships-" + RequestingAgentID.ToString()))
m_Cache.Remove("memberships-" + RequestingAgentID.ToString());
}
return group.GroupID;
}
public bool UpdateGroup(UUID groupID, GroupRecordDelegate d)
{
//reason = string.Empty;
//ExtendedGroupRecord group = m_GroupsService.UpdateGroup(RequestingAgentID, groupID, charter, showInList, insigniaID, membershipFee, openEnrollment, allowPublish, maturePublish);
ExtendedGroupRecord group = d();
if (group != null && group.GroupID != UUID.Zero)
lock (m_Cache)
m_Cache.AddOrUpdate("group-" + group.GroupID.ToString(), group, GROUPS_CACHE_TIMEOUT);
return true;
}
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string GroupName, GroupRecordDelegate d)
{
//if (GroupID == UUID.Zero && (GroupName == null || GroupName != null && GroupName == string.Empty))
// return null;
object group = null;
bool firstCall = false;
string cacheKey = "group-";
if (GroupID != UUID.Zero)
cacheKey += GroupID.ToString();
else
cacheKey += GroupName;
//m_log.DebugFormat("[XXX]: GetGroupRecord {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out group))
{
//m_log.DebugFormat("[XXX]: GetGroupRecord {0} cached!", cacheKey);
return (ExtendedGroupRecord)group;
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
//group = m_GroupsService.GetGroupRecord(RequestingAgentID, GroupID, GroupName);
group = d();
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, group, GROUPS_CACHE_TIMEOUT);
return (ExtendedGroupRecord)group;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, GroupMembershipDelegate d)
{
GroupMembershipData membership = d();
if (membership == null)
return false;
lock (m_Cache)
{
// first, remove everything! add a user is a heavy-duty op
m_Cache.Clear();
m_Cache.AddOrUpdate("active-" + AgentID.ToString(), membership, GROUPS_CACHE_TIMEOUT);
m_Cache.AddOrUpdate("membership-" + AgentID.ToString() + "-" + GroupID.ToString(), membership, GROUPS_CACHE_TIMEOUT);
}
return true;
}
public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, VoidDelegate d)
{
d();
lock (m_Cache)
{
string cacheKey = "active-" + AgentID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "memberships-" + AgentID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "members-" + RequestingAgentID.ToString() + "-" + GroupID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "roles-" + "-" + GroupID.ToString() + "-" + AgentID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
}
}
public void SetAgentActiveGroup(string AgentID, GroupMembershipDelegate d)
{
GroupMembershipData activeGroup = d();
string cacheKey = "active-" + AgentID.ToString();
lock (m_Cache)
if (m_Cache.Contains(cacheKey))
m_Cache.AddOrUpdate(cacheKey, activeGroup, GROUPS_CACHE_TIMEOUT);
}
public ExtendedGroupMembershipData GetAgentActiveMembership(string AgentID, GroupMembershipDelegate d)
{
object membership = null;
bool firstCall = false;
string cacheKey = "active-" + AgentID.ToString();
//m_log.DebugFormat("[XXX]: GetAgentActiveMembership {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out membership))
{
//m_log.DebugFormat("[XXX]: GetAgentActiveMembership {0} cached!", cacheKey);
return (ExtendedGroupMembershipData)membership;
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
membership = d();
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, membership, GROUPS_CACHE_TIMEOUT);
return (ExtendedGroupMembershipData)membership;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public ExtendedGroupMembershipData GetAgentGroupMembership(string AgentID, UUID GroupID, GroupMembershipDelegate d)
{
object membership = null;
bool firstCall = false;
string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString();
//m_log.DebugFormat("[XXX]: GetAgentGroupMembership {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out membership))
{
//m_log.DebugFormat("[XXX]: GetAgentGroupMembership {0}", cacheKey);
return (ExtendedGroupMembershipData)membership;
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
membership = d();
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, membership, GROUPS_CACHE_TIMEOUT);
return (ExtendedGroupMembershipData)membership;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public List<GroupMembershipData> GetAgentGroupMemberships(string AgentID, GroupMembershipListDelegate d)
{
object memberships = null;
bool firstCall = false;
string cacheKey = "memberships-" + AgentID.ToString();
//m_log.DebugFormat("[XXX]: GetAgentGroupMemberships {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out memberships))
{
//m_log.DebugFormat("[XXX]: GetAgentGroupMemberships {0} cached!", cacheKey);
return (List<GroupMembershipData>)memberships;
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
memberships = d();
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, memberships, GROUPS_CACHE_TIMEOUT);
return (List<GroupMembershipData>)memberships;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public List<GroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID, GroupMembersListDelegate d)
{
object members = null;
bool firstCall = false;
// we need to key in also on the requester, because different ppl have different view privileges
string cacheKey = "members-" + RequestingAgentID.ToString() + "-" + GroupID.ToString();
//m_log.DebugFormat("[XXX]: GetGroupMembers {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out members))
{
List<ExtendedGroupMembersData> xx = (List<ExtendedGroupMembersData>)members;
return xx.ConvertAll<GroupMembersData>(new Converter<ExtendedGroupMembersData, GroupMembersData>(m_ForeignImporter.ConvertGroupMembersData));
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
List<ExtendedGroupMembersData> _members = d();
if (_members != null && _members.Count > 0)
members = _members.ConvertAll<GroupMembersData>(new Converter<ExtendedGroupMembersData, GroupMembersData>(m_ForeignImporter.ConvertGroupMembersData));
else
members = new List<GroupMembersData>();
lock (m_Cache)
{
//m_Cache.AddOrUpdate(cacheKey, members, GROUPS_CACHE_TIMEOUT);
m_Cache.AddOrUpdate(cacheKey, _members, GROUPS_CACHE_TIMEOUT);
return (List<GroupMembersData>)members;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public bool AddGroupRole(UUID groupID, UUID roleID, string description, string name, ulong powers, string title, BooleanDelegate d)
{
if (d())
{
GroupRolesData role = new GroupRolesData();
role.Description = description;
role.Members = 0;
role.Name = name;
role.Powers = powers;
role.RoleID = roleID;
role.Title = title;
lock (m_Cache)
{
m_Cache.AddOrUpdate("role-" + roleID.ToString(), role, GROUPS_CACHE_TIMEOUT);
// also remove this list
if (m_Cache.Contains("roles-" + groupID.ToString()))
m_Cache.Remove("roles-" + groupID.ToString());
}
return true;
}
return false;
}
public bool UpdateGroupRole(UUID groupID, UUID roleID, string name, string description, string title, ulong powers, BooleanDelegate d)
{
if (d())
{
object role;
lock (m_Cache)
if (m_Cache.TryGetValue("role-" + roleID.ToString(), out role))
{
GroupRolesData r = (GroupRolesData)role;
r.Description = description;
r.Name = name;
r.Powers = powers;
r.Title = title;
m_Cache.Update("role-" + roleID.ToString(), r, GROUPS_CACHE_TIMEOUT);
}
return true;
}
else
{
lock (m_Cache)
{
if (m_Cache.Contains("role-" + roleID.ToString()))
m_Cache.Remove("role-" + roleID.ToString());
// also remove these lists, because they will have an outdated role
if (m_Cache.Contains("roles-" + groupID.ToString()))
m_Cache.Remove("roles-" + groupID.ToString());
}
return false;
}
}
public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, VoidDelegate d)
{
d();
lock (m_Cache)
{
if (m_Cache.Contains("role-" + roleID.ToString()))
m_Cache.Remove("role-" + roleID.ToString());
// also remove the list, because it will have an removed role
if (m_Cache.Contains("roles-" + groupID.ToString()))
m_Cache.Remove("roles-" + groupID.ToString());
if (m_Cache.Contains("roles-" + groupID.ToString() + "-" + RequestingAgentID.ToString()))
m_Cache.Remove("roles-" + groupID.ToString() + "-" + RequestingAgentID.ToString());
if (m_Cache.Contains("rolemembers-" + RequestingAgentID.ToString() + "-" + groupID.ToString()))
m_Cache.Remove("rolemembers-" + RequestingAgentID.ToString() + "-" + groupID.ToString());
}
}
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID, GroupRolesListDelegate d)
{
object roles = null;
bool firstCall = false;
string cacheKey = "roles-" + GroupID.ToString();
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out roles))
return (List<GroupRolesData>)roles;
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
roles = d();
if (roles != null)
{
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, roles, GROUPS_CACHE_TIMEOUT);
return (List<GroupRolesData>)roles;
}
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public List<GroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, RoleMembersListDelegate d)
{
object rmembers = null;
bool firstCall = false;
// we need to key in also on the requester, because different ppl have different view privileges
string cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString();
//m_log.DebugFormat("[XXX]: GetGroupRoleMembers {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out rmembers))
{
List<ExtendedGroupRoleMembersData> xx = (List<ExtendedGroupRoleMembersData>)rmembers;
return xx.ConvertAll<GroupRoleMembersData>(m_ForeignImporter.ConvertGroupRoleMembersData);
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
List<ExtendedGroupRoleMembersData> _rmembers = d();
if (_rmembers != null && _rmembers.Count > 0)
rmembers = _rmembers.ConvertAll<GroupRoleMembersData>(new Converter<ExtendedGroupRoleMembersData, GroupRoleMembersData>(m_ForeignImporter.ConvertGroupRoleMembersData));
else
rmembers = new List<GroupRoleMembersData>();
lock (m_Cache)
{
// For some strange reason, when I cache the list of GroupRoleMembersData,
// it gets emptied out. The TryGet gets an empty list...
//m_Cache.AddOrUpdate(cacheKey, rmembers, GROUPS_CACHE_TIMEOUT);
// Caching the list of ExtendedGroupRoleMembersData doesn't show that issue
// I don't get it.
m_Cache.AddOrUpdate(cacheKey, _rmembers, GROUPS_CACHE_TIMEOUT);
return (List<GroupRoleMembersData>)rmembers;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public void AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, BooleanDelegate d)
{
if (d())
{
lock (m_Cache)
{
// update the cached role
string cacheKey = "role-" + RoleID.ToString();
object obj;
if (m_Cache.TryGetValue(cacheKey, out obj))
{
GroupRolesData r = (GroupRolesData)obj;
r.Members++;
}
// add this agent to the list of role members
cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString();
if (m_Cache.TryGetValue(cacheKey, out obj))
{
try
{
// This may throw an exception, in which case the agentID is not a UUID but a full ID
// In that case, let's just remove the whoe things from the cache
UUID id = new UUID(AgentID);
List<ExtendedGroupRoleMembersData> xx = (List<ExtendedGroupRoleMembersData>)obj;
List<GroupRoleMembersData> rmlist = xx.ConvertAll<GroupRoleMembersData>(m_ForeignImporter.ConvertGroupRoleMembersData);
GroupRoleMembersData rm = new GroupRoleMembersData();
rm.MemberID = id;
rm.RoleID = RoleID;
rmlist.Add(rm);
}
catch
{
m_Cache.Remove(cacheKey);
}
}
// Remove the cached info about this agent's roles
// because we don't have enough local info about the new role
cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
}
}
}
public void RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, BooleanDelegate d)
{
if (d())
{
lock (m_Cache)
{
// update the cached role
string cacheKey = "role-" + RoleID.ToString();
object obj;
if (m_Cache.TryGetValue(cacheKey, out obj))
{
GroupRolesData r = (GroupRolesData)obj;
r.Members--;
}
cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "rolemembers-" + RequestingAgentID.ToString() + "-" + GroupID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
}
}
}
public List<GroupRolesData> GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID, GroupRolesListDelegate d)
{
object roles = null;
bool firstCall = false;
string cacheKey = "roles-" + GroupID.ToString() + "-" + AgentID.ToString();
//m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out roles))
{
//m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0} cached!", cacheKey);
return (List<GroupRolesData>)roles;
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
roles = d();
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, roles, GROUPS_CACHE_TIMEOUT);
m_ActiveRequests.Remove(cacheKey);
return (List<GroupRolesData>)roles;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public void SetAgentActiveGroupRole(string AgentID, UUID GroupID, VoidDelegate d)
{
d();
lock (m_Cache)
{
// Invalidate cached info, because it has ActiveRoleID and Powers
string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "memberships-" + AgentID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
}
}
public void UpdateMembership(string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile, VoidDelegate d)
{
d();
lock (m_Cache)
{
string cacheKey = "membership-" + AgentID.ToString() + "-" + GroupID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "memberships-" + AgentID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
cacheKey = "active-" + AgentID.ToString();
object m = null;
if (m_Cache.TryGetValue(cacheKey, out m))
{
GroupMembershipData membership = (GroupMembershipData)m;
membership.ListInProfile = ListInProfile;
membership.AcceptNotices = AcceptNotices;
}
}
}
public bool AddGroupNotice(UUID groupID, UUID noticeID, GroupNoticeInfo notice, BooleanDelegate d)
{
if (d())
{
lock (m_Cache)
{
m_Cache.AddOrUpdate("notice-" + noticeID.ToString(), notice, GROUPS_CACHE_TIMEOUT);
string cacheKey = "notices-" + groupID.ToString();
if (m_Cache.Contains(cacheKey))
m_Cache.Remove(cacheKey);
}
return true;
}
return false;
}
public GroupNoticeInfo GetGroupNotice(UUID noticeID, NoticeDelegate d)
{
object notice = null;
bool firstCall = false;
string cacheKey = "notice-" + noticeID.ToString();
//m_log.DebugFormat("[XXX]: GetAgentGroupRoles {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out notice))
{
return (GroupNoticeInfo)notice;
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
GroupNoticeInfo _notice = d();
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, _notice, GROUPS_CACHE_TIMEOUT);
return _notice;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
public List<ExtendedGroupNoticeData> GetGroupNotices(UUID GroupID, NoticeListDelegate d)
{
object notices = null;
bool firstCall = false;
string cacheKey = "notices-" + GroupID.ToString();
//m_log.DebugFormat("[XXX]: GetGroupNotices {0}", cacheKey);
while (true)
{
lock (m_Cache)
{
if (m_Cache.TryGetValue(cacheKey, out notices))
{
//m_log.DebugFormat("[XXX]: GetGroupNotices {0} cached!", cacheKey);
return (List<ExtendedGroupNoticeData>)notices;
}
// not cached
if (!m_ActiveRequests.ContainsKey(cacheKey))
{
m_ActiveRequests.Add(cacheKey, true);
firstCall = true;
}
}
if (firstCall)
{
try
{
notices = d();
lock (m_Cache)
{
m_Cache.AddOrUpdate(cacheKey, notices, GROUPS_CACHE_TIMEOUT);
return (List<ExtendedGroupNoticeData>)notices;
}
}
finally
{
m_ActiveRequests.Remove(cacheKey);
}
}
else
Thread.Sleep(50);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,101 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Data;
using OpenSim.Services.Interfaces;
using OpenSim.Services.Base;
namespace OpenSim.Groups
{
public class GroupsServiceBase : ServiceBase
{
protected IGroupsData m_Database = null;
protected IGridUserData m_GridUserService = null;
public GroupsServiceBase(IConfigSource config, string cName)
: base(config)
{
string dllName = String.Empty;
string connString = String.Empty;
string realm = "os_groups";
string usersRealm = "GridUser";
string configName = (cName == string.Empty) ? "Groups" : cName;
//
// Try reading the [DatabaseService] section, if it exists
//
IConfig dbConfig = config.Configs["DatabaseService"];
if (dbConfig != null)
{
if (dllName == String.Empty)
dllName = dbConfig.GetString("StorageProvider", String.Empty);
if (connString == String.Empty)
connString = dbConfig.GetString("ConnectionString", String.Empty);
}
//
// [Groups] section overrides [DatabaseService], if it exists
//
IConfig groupsConfig = config.Configs[configName];
if (groupsConfig != null)
{
dllName = groupsConfig.GetString("StorageProvider", dllName);
connString = groupsConfig.GetString("ConnectionString", connString);
realm = groupsConfig.GetString("Realm", realm);
}
//
// We tried, but this doesn't exist. We can't proceed.
//
if (dllName.Equals(String.Empty))
throw new Exception("No StorageProvider configured");
m_Database = LoadPlugin<IGroupsData>(dllName, new Object[] { connString, realm });
if (m_Database == null)
throw new Exception("Could not find a storage interface in the given module " + dllName);
//
// [GridUserService] section overrides [DatabaseService], if it exists
//
IConfig usersConfig = config.Configs["GridUserService"];
if (usersConfig != null)
{
dllName = usersConfig.GetString("StorageProvider", dllName);
connString = usersConfig.GetString("ConnectionString", connString);
usersRealm = usersConfig.GetString("Realm", usersRealm);
}
m_GridUserService = LoadPlugin<IGridUserData>(dllName, new Object[] { connString, usersRealm });
if (m_GridUserService == null)
throw new Exception("Could not find a storage inferface for the given users module " + dllName);
}
}
}

View File

@ -1,361 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Timers;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Services.Interfaces;
namespace OpenSim.Groups
{
public class HGGroupsService : GroupsService
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IOfflineIMService m_OfflineIM;
private IUserAccountService m_UserAccounts;
private string m_HomeURI;
public HGGroupsService(IConfigSource config, IOfflineIMService im, IUserAccountService users, string homeURI)
: base(config, string.Empty)
{
m_OfflineIM = im;
m_UserAccounts = users;
m_HomeURI = homeURI;
if (!m_HomeURI.EndsWith("/"))
m_HomeURI += "/";
}
#region HG specific operations
public bool CreateGroupProxy(string RequestingAgentID, string agentID, string accessToken, UUID groupID, string serviceLocation, string name, out string reason)
{
reason = string.Empty;
Uri uri = null;
try
{
uri = new Uri(serviceLocation);
}
catch (UriFormatException)
{
reason = "Bad location for group proxy";
return false;
}
// Check if it already exists
GroupData grec = m_Database.RetrieveGroup(groupID);
if (grec == null ||
(grec != null && grec.Data["Location"] != string.Empty && grec.Data["Location"].ToLower() != serviceLocation.ToLower()))
{
// Create the group
grec = new GroupData();
grec.GroupID = groupID;
grec.Data = new Dictionary<string, string>();
grec.Data["Name"] = name + " @ " + uri.Authority;
grec.Data["Location"] = serviceLocation;
grec.Data["Charter"] = string.Empty;
grec.Data["InsigniaID"] = UUID.Zero.ToString();
grec.Data["FounderID"] = UUID.Zero.ToString();
grec.Data["MembershipFee"] = "0";
grec.Data["OpenEnrollment"] = "0";
grec.Data["ShowInList"] = "0";
grec.Data["AllowPublish"] = "0";
grec.Data["MaturePublish"] = "0";
grec.Data["OwnerRoleID"] = UUID.Zero.ToString();
if (!m_Database.StoreGroup(grec))
return false;
}
if (grec.Data["Location"] == string.Empty)
{
reason = "Cannot add proxy membership to non-proxy group";
return false;
}
UUID uid = UUID.Zero;
string url = string.Empty, first = string.Empty, last = string.Empty, tmp = string.Empty;
Util.ParseUniversalUserIdentifier(RequestingAgentID, out uid, out url, out first, out last, out tmp);
string fromName = first + "." + last + "@" + url;
// Invite to group again
InviteToGroup(fromName, groupID, new UUID(agentID), grec.Data["Name"]);
// Stick the proxy membership in the DB already
// we'll delete it if the agent declines the invitation
MembershipData membership = new MembershipData();
membership.PrincipalID = agentID;
membership.GroupID = groupID;
membership.Data = new Dictionary<string, string>();
membership.Data["SelectedRoleID"] = UUID.Zero.ToString();
membership.Data["Contribution"] = "0";
membership.Data["ListInProfile"] = "1";
membership.Data["AcceptNotices"] = "1";
membership.Data["AccessToken"] = accessToken;
m_Database.StoreMember(membership);
return true;
}
public bool RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID, string token)
{
// check the token
MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID);
if (membership != null)
{
if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
{
return RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID);
}
else
{
m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
return false;
}
}
else
{
m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", AgentID);
return false;
}
}
public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID, string groupName, string token)
{
// check the token
if (!VerifyToken(GroupID, RequestingAgentID, token))
return null;
ExtendedGroupRecord grec;
if (GroupID == UUID.Zero)
grec = GetGroupRecord(RequestingAgentID, groupName);
else
grec = GetGroupRecord(RequestingAgentID, GroupID);
if (grec != null)
FillFounderUUI(grec);
return grec;
}
public List<ExtendedGroupMembersData> GetGroupMembers(string RequestingAgentID, UUID GroupID, string token)
{
if (!VerifyToken(GroupID, RequestingAgentID, token))
return new List<ExtendedGroupMembersData>();
List<ExtendedGroupMembersData> members = GetGroupMembers(RequestingAgentID, GroupID);
// convert UUIDs to UUIs
members.ForEach(delegate (ExtendedGroupMembersData m)
{
if (m.AgentID.ToString().Length == 36) // UUID
{
UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.AgentID));
if (account != null)
m.AgentID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
}
});
return members;
}
public List<GroupRolesData> GetGroupRoles(string RequestingAgentID, UUID GroupID, string token)
{
if (!VerifyToken(GroupID, RequestingAgentID, token))
return new List<GroupRolesData>();
return GetGroupRoles(RequestingAgentID, GroupID);
}
public List<ExtendedGroupRoleMembersData> GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token)
{
if (!VerifyToken(GroupID, RequestingAgentID, token))
return new List<ExtendedGroupRoleMembersData>();
List<ExtendedGroupRoleMembersData> rolemembers = GetGroupRoleMembers(RequestingAgentID, GroupID);
// convert UUIDs to UUIs
rolemembers.ForEach(delegate(ExtendedGroupRoleMembersData m)
{
if (m.MemberID.ToString().Length == 36) // UUID
{
UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, new UUID(m.MemberID));
if (account != null)
m.MemberID = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
}
});
return rolemembers;
}
public bool AddNotice(string RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message,
bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID)
{
// check that the group proxy exists
ExtendedGroupRecord grec = GetGroupRecord(RequestingAgentID, groupID);
if (grec == null)
{
m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to non-existent group proxy");
return false;
}
// check that the group is remote
if (grec.ServiceLocation == string.Empty)
{
m_log.DebugFormat("[Groups.HGGroupsService]: attempt at adding notice to local (non-proxy) group");
return false;
}
// check that there isn't already a notice with the same ID
if (GetGroupNotice(RequestingAgentID, noticeID) != null)
{
m_log.DebugFormat("[Groups.HGGroupsService]: a notice with the same ID already exists", grec.ServiceLocation);
return false;
}
// This has good intentions (security) but it will potentially DDS the origin...
// We'll need to send a proof along with the message. Maybe encrypt the message
// using key pairs
//
//// check that the notice actually exists in the origin
//GroupsServiceHGConnector c = new GroupsServiceHGConnector(grec.ServiceLocation);
//if (!c.VerifyNotice(noticeID, groupID))
//{
// m_log.DebugFormat("[Groups.HGGroupsService]: notice does not exist at origin {0}", grec.ServiceLocation);
// return false;
//}
// ok, we're good!
return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID);
}
public bool VerifyNotice(UUID noticeID, UUID groupID)
{
GroupNoticeInfo notice = GetGroupNotice(string.Empty, noticeID);
if (notice == null)
return false;
if (notice.GroupID != groupID)
return false;
return true;
}
#endregion
private void InviteToGroup(string fromName, UUID groupID, UUID invitedAgentID, string groupName)
{
// Todo: Security check, probably also want to send some kind of notification
UUID InviteID = UUID.Random();
if (AddAgentToGroupInvite(InviteID, groupID, invitedAgentID.ToString()))
{
Guid inviteUUID = InviteID.Guid;
GridInstantMessage msg = new GridInstantMessage();
msg.imSessionID = inviteUUID;
// msg.fromAgentID = agentID.Guid;
msg.fromAgentID = groupID.Guid;
msg.toAgentID = invitedAgentID.Guid;
//msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
msg.timestamp = 0;
msg.fromAgentName = fromName;
msg.message = string.Format("Please confirm your acceptance to join group {0}.", groupName);
msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation;
msg.fromGroup = true;
msg.offline = (byte)0;
msg.ParentEstateID = 0;
msg.Position = Vector3.Zero;
msg.RegionID = UUID.Zero.Guid;
msg.binaryBucket = new byte[20];
string reason = string.Empty;
m_OfflineIM.StoreMessage(msg, out reason);
}
}
private bool AddAgentToGroupInvite(UUID inviteID, UUID groupID, string agentID)
{
// Check whether the invitee is already a member of the group
MembershipData m = m_Database.RetrieveMember(groupID, agentID);
if (m != null)
return false;
// Check whether there are pending invitations and delete them
InvitationData invite = m_Database.RetrieveInvitation(groupID, agentID);
if (invite != null)
m_Database.DeleteInvite(invite.InviteID);
invite = new InvitationData();
invite.InviteID = inviteID;
invite.PrincipalID = agentID;
invite.GroupID = groupID;
invite.RoleID = UUID.Zero;
invite.Data = new Dictionary<string, string>();
return m_Database.StoreInvitation(invite);
}
private void FillFounderUUI(ExtendedGroupRecord grec)
{
UserAccount account = m_UserAccounts.GetUserAccount(UUID.Zero, grec.FounderID);
if (account != null)
grec.FounderUUI = Util.UniversalIdentifier(account.PrincipalID, account.FirstName, account.LastName, m_HomeURI);
}
private bool VerifyToken(UUID groupID, string agentID, string token)
{
// check the token
MembershipData membership = m_Database.RetrieveMember(groupID, agentID);
if (membership != null)
{
if (token != string.Empty && token.Equals(membership.Data["AccessToken"]))
return true;
else
m_log.DebugFormat("[Groups.HGGroupsService]: access token {0} did not match stored one {1}", token, membership.Data["AccessToken"]);
}
else
m_log.DebugFormat("[Groups.HGGroupsService]: membership not found for {0}", agentID);
return false;
}
}
}

View File

@ -1,252 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using Mono.Addins;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Client;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
namespace OpenSim.OfflineIM
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OfflineIMConnectorModule")]
public class OfflineIMRegionModule : ISharedRegionModule, IOfflineIMService
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private bool m_Enabled = false;
private List<Scene> m_SceneList = new List<Scene>();
IMessageTransferModule m_TransferModule = null;
private bool m_ForwardOfflineGroupMessages = true;
private IOfflineIMService m_OfflineIMService;
public void Initialise(IConfigSource config)
{
IConfig cnf = config.Configs["Messaging"];
if (cnf == null)
return;
if (cnf != null && cnf.GetString("OfflineMessageModule", string.Empty) != Name)
return;
m_Enabled = true;
string serviceLocation = cnf.GetString("OfflineMessageURL", string.Empty);
if (serviceLocation == string.Empty)
m_OfflineIMService = new OfflineIMService(config);
else
m_OfflineIMService = new OfflineIMServiceRemoteConnector(config);
m_ForwardOfflineGroupMessages = cnf.GetBoolean("ForwardOfflineGroupMessages", m_ForwardOfflineGroupMessages);
m_log.DebugFormat("[OfflineIM.V2]: Offline messages enabled by {0}", Name);
}
public void AddRegion(Scene scene)
{
if (!m_Enabled)
return;
scene.RegisterModuleInterface<IOfflineIMService>(this);
m_SceneList.Add(scene);
scene.EventManager.OnNewClient += OnNewClient;
}
public void RegionLoaded(Scene scene)
{
if (!m_Enabled)
return;
if (m_TransferModule == null)
{
m_TransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
if (m_TransferModule == null)
{
scene.EventManager.OnNewClient -= OnNewClient;
m_SceneList.Clear();
m_log.Error("[OfflineIM.V2]: No message transfer module is enabled. Disabling offline messages");
}
m_TransferModule.OnUndeliveredMessage += UndeliveredMessage;
}
}
public void RemoveRegion(Scene scene)
{
if (!m_Enabled)
return;
m_SceneList.Remove(scene);
scene.EventManager.OnNewClient -= OnNewClient;
m_TransferModule.OnUndeliveredMessage -= UndeliveredMessage;
scene.ForEachClient(delegate(IClientAPI client)
{
client.OnRetrieveInstantMessages -= RetrieveInstantMessages;
});
}
public void PostInitialise()
{
}
public string Name
{
get { return "Offline Message Module V2"; }
}
public Type ReplaceableInterface
{
get { return null; }
}
public void Close()
{
m_SceneList.Clear();
}
private Scene FindScene(UUID agentID)
{
foreach (Scene s in m_SceneList)
{
ScenePresence presence = s.GetScenePresence(agentID);
if (presence != null && !presence.IsChildAgent)
return s;
}
return null;
}
private IClientAPI FindClient(UUID agentID)
{
foreach (Scene s in m_SceneList)
{
ScenePresence presence = s.GetScenePresence(agentID);
if (presence != null && !presence.IsChildAgent)
return presence.ControllingClient;
}
return null;
}
private void OnNewClient(IClientAPI client)
{
client.OnRetrieveInstantMessages += RetrieveInstantMessages;
}
private void RetrieveInstantMessages(IClientAPI client)
{
m_log.DebugFormat("[OfflineIM.V2]: Retrieving stored messages for {0}", client.AgentId);
List<GridInstantMessage> msglist = m_OfflineIMService.GetMessages(client.AgentId);
if (msglist == null)
m_log.DebugFormat("[OfflineIM.V2]: WARNING null message list.");
foreach (GridInstantMessage im in msglist)
{
if (im.dialog == (byte)InstantMessageDialog.InventoryOffered)
// send it directly or else the item will be given twice
client.SendInstantMessage(im);
else
{
// Send through scene event manager so all modules get a chance
// to look at this message before it gets delivered.
//
// Needed for proper state management for stored group
// invitations
//
Scene s = FindScene(client.AgentId);
if (s != null)
s.EventManager.TriggerIncomingInstantMessage(im);
}
}
}
private void UndeliveredMessage(GridInstantMessage im)
{
if (im.dialog != (byte)InstantMessageDialog.MessageFromObject &&
im.dialog != (byte)InstantMessageDialog.MessageFromAgent &&
im.dialog != (byte)InstantMessageDialog.GroupNotice &&
im.dialog != (byte)InstantMessageDialog.GroupInvitation &&
im.dialog != (byte)InstantMessageDialog.InventoryOffered)
{
return;
}
if (!m_ForwardOfflineGroupMessages)
{
if (im.dialog == (byte)InstantMessageDialog.GroupNotice ||
im.dialog == (byte)InstantMessageDialog.GroupInvitation)
return;
}
string reason = string.Empty;
bool success = m_OfflineIMService.StoreMessage(im, out reason);
if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent)
{
IClientAPI client = FindClient(new UUID(im.fromAgentID));
if (client == null)
return;
client.SendInstantMessage(new GridInstantMessage(
null, new UUID(im.toAgentID),
"System", new UUID(im.fromAgentID),
(byte)InstantMessageDialog.MessageFromAgent,
"User is not logged in. " +
(success ? "Message saved." : "Message not saved: " + reason),
false, new Vector3()));
}
}
#region IOfflineIM
public List<GridInstantMessage> GetMessages(UUID principalID)
{
return m_OfflineIMService.GetMessages(principalID);
}
public bool StoreMessage(GridInstantMessage im, out string reason)
{
return m_OfflineIMService.StoreMessage(im, out reason);
}
public void DeleteMessages(UUID userID)
{
m_OfflineIMService.DeleteMessages(userID);
}
#endregion
}
}

View File

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Mono.Addins;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenSim.Addons.OfflineIM")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("http://opensimulator.org")]
[assembly: AssemblyProduct("OpenSim.Addons.OfflineIM")]
[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a16a9905-4393-4872-9fca-4c81bedbd9f2")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]
[assembly: Addin("OpenSim.OfflineIM", OpenSim.VersionInfo.VersionNumber)]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]

View File

@ -1,171 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using OpenSim.Framework;
using OpenSim.Framework.ServiceAuth;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using OpenMetaverse;
using log4net;
using Nini.Config;
namespace OpenSim.OfflineIM
{
public class OfflineIMServiceRemoteConnector : IOfflineIMService
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private string m_ServerURI = string.Empty;
private IServiceAuth m_Auth;
private object m_Lock = new object();
public OfflineIMServiceRemoteConnector(string url)
{
m_ServerURI = url;
m_log.DebugFormat("[OfflineIM.V2.RemoteConnector]: Offline IM server at {0}", m_ServerURI);
}
public OfflineIMServiceRemoteConnector(IConfigSource config)
{
IConfig cnf = config.Configs["Messaging"];
if (cnf == null)
{
m_log.WarnFormat("[OfflineIM.V2.RemoteConnector]: Missing Messaging configuration");
return;
}
m_ServerURI = cnf.GetString("OfflineMessageURL", string.Empty);
/// This is from BaseServiceConnector
string authType = Util.GetConfigVarFromSections<string>(config, "AuthType", new string[] { "Network", "Messaging" }, "None");
switch (authType)
{
case "BasicHttpAuthentication":
m_Auth = new BasicHttpAuthentication(config, "Messaging");
break;
}
///
m_log.DebugFormat("[OfflineIM.V2.RemoteConnector]: Offline IM server at {0} with auth {1}",
m_ServerURI, (m_Auth == null ? "None" : m_Auth.GetType().ToString()));
}
#region IOfflineIMService
public List<GridInstantMessage> GetMessages(UUID principalID)
{
List<GridInstantMessage> ims = new List<GridInstantMessage>();
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["PrincipalID"] = principalID;
Dictionary<string, object> ret = MakeRequest("GET", sendData);
if (ret == null)
return ims;
if (!ret.ContainsKey("RESULT"))
return ims;
string result = ret["RESULT"].ToString();
if (result == "NULL" || result.ToLower() == "false")
{
string reason = ret.ContainsKey("REASON") ? ret["REASON"].ToString() : "Unknown error";
m_log.DebugFormat("[OfflineIM.V2.RemoteConnector]: GetMessages for {0} failed: {1}", principalID, reason);
return ims;
}
foreach (object v in ((Dictionary<string, object>)ret["RESULT"]).Values)
{
GridInstantMessage m = OfflineIMDataUtils.GridInstantMessage((Dictionary<string, object>)v);
ims.Add(m);
}
return ims;
}
public bool StoreMessage(GridInstantMessage im, out string reason)
{
reason = string.Empty;
Dictionary<string, object> sendData = OfflineIMDataUtils.GridInstantMessage(im);
Dictionary<string, object> ret = MakeRequest("STORE", sendData);
if (ret == null)
{
reason = "Bad response from server";
return false;
}
string result = ret["RESULT"].ToString();
if (result == "NULL" || result.ToLower() == "false")
{
reason = ret.ContainsKey("REASON") ? ret["REASON"].ToString() : "Unknown error";
return false;
}
return true;
}
public void DeleteMessages(UUID userID)
{
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["UserID"] = userID;
MakeRequest("DELETE", sendData);
}
#endregion
#region Make Request
private Dictionary<string, object> MakeRequest(string method, Dictionary<string, object> sendData)
{
sendData["METHOD"] = method;
string reply = string.Empty;
lock (m_Lock)
reply = SynchronousRestFormsRequester.MakeRequest("POST",
m_ServerURI + "/offlineim",
ServerUtils.BuildQueryString(sendData),
m_Auth);
Dictionary<string, object> replyData = ServerUtils.ParseXmlResponse(
reply);
return replyData;
}
#endregion
}
}

View File

@ -1,223 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Framework.ServiceAuth;
using OpenSim.Server.Handlers.Base;
using log4net;
using OpenMetaverse;
namespace OpenSim.OfflineIM
{
public class OfflineIMServiceRobustConnector : ServiceConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IOfflineIMService m_OfflineIMService;
private string m_ConfigName = "Messaging";
public OfflineIMServiceRobustConnector(IConfigSource config, IHttpServer server, string configName) :
base(config, server, configName)
{
if (configName != String.Empty)
m_ConfigName = configName;
m_log.DebugFormat("[OfflineIM.V2.RobustConnector]: Starting with config name {0}", m_ConfigName);
m_OfflineIMService = new OfflineIMService(config);
IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName);
server.AddStreamHandler(new OfflineIMServicePostHandler(m_OfflineIMService, auth));
}
}
public class OfflineIMServicePostHandler : BaseStreamHandler
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IOfflineIMService m_OfflineIMService;
public OfflineIMServicePostHandler(IOfflineIMService service, IServiceAuth auth) :
base("POST", "/offlineim", auth)
{
m_OfflineIMService = service;
}
protected override byte[] ProcessRequest(string path, Stream requestData,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
StreamReader sr = new StreamReader(requestData);
string body = sr.ReadToEnd();
sr.Close();
body = body.Trim();
//m_log.DebugFormat("[XXX]: query String: {0}", body);
try
{
Dictionary<string, object> request =
ServerUtils.ParseQueryString(body);
if (!request.ContainsKey("METHOD"))
return FailureResult();
string method = request["METHOD"].ToString();
request.Remove("METHOD");
switch (method)
{
case "GET":
return HandleGet(request);
case "STORE":
return HandleStore(request);
case "DELETE":
return HandleDelete(request);
}
m_log.DebugFormat("[OFFLINE IM HANDLER]: unknown method request: {0}", method);
}
catch (Exception e)
{
m_log.Error(string.Format("[OFFLINE IM HANDLER]: Exception {0} ", e.Message), e);
}
return FailureResult();
}
byte[] HandleStore(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
GridInstantMessage im = OfflineIMDataUtils.GridInstantMessage(request);
string reason = string.Empty;
bool success = m_OfflineIMService.StoreMessage(im, out reason);
result["RESULT"] = success.ToString();
if (!success)
result["REASON"] = reason;
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleGet(Dictionary<string, object> request)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (!request.ContainsKey("PrincipalID"))
NullResult(result, "Bad network data");
else
{
UUID principalID = new UUID(request["PrincipalID"].ToString());
List<GridInstantMessage> ims = m_OfflineIMService.GetMessages(principalID);
Dictionary<string, object> dict = new Dictionary<string, object>();
int i = 0;
foreach (GridInstantMessage m in ims)
dict["im-" + i++] = OfflineIMDataUtils.GridInstantMessage(m);
result["RESULT"] = dict;
}
string xmlString = ServerUtils.BuildXmlResponse(result);
//m_log.DebugFormat("[XXX]: resp string: {0}", xmlString);
return Util.UTF8NoBomEncoding.GetBytes(xmlString);
}
byte[] HandleDelete(Dictionary<string, object> request)
{
if (!request.ContainsKey("UserID"))
{
return FailureResult();
}
else
{
UUID userID = new UUID(request["UserID"].ToString());
m_OfflineIMService.DeleteMessages(userID);
return SuccessResult();
}
}
#region Helpers
private void NullResult(Dictionary<string, object> result, string reason)
{
result["RESULT"] = "NULL";
result["REASON"] = reason;
}
private byte[] FailureResult()
{
return BoolResult(false);
}
private byte[] SuccessResult()
{
return BoolResult(true);
}
private byte[] BoolResult(bool value)
{
XmlDocument doc = new XmlDocument();
XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration,
"", "");
doc.AppendChild(xmlnode);
XmlElement rootElement = doc.CreateElement("", "ServerResponse",
"");
doc.AppendChild(rootElement);
XmlElement result = doc.CreateElement("", "RESULT", "");
result.AppendChild(doc.CreateTextNode(value.ToString()));
rootElement.AppendChild(result);
return Util.DocToBytes(doc);
}
#endregion
}
}

View File

@ -1,134 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Timers;
using System.Xml;
using System.Xml.Serialization;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Data;
using OpenSim.Framework;
using OpenSim.Services.Interfaces;
namespace OpenSim.OfflineIM
{
public class OfflineIMService : OfflineIMServiceBase, IOfflineIMService
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const int MAX_IM = 25;
private XmlSerializer m_serializer;
private static bool m_Initialized = false;
public OfflineIMService(IConfigSource config)
: base(config)
{
m_serializer = new XmlSerializer(typeof(GridInstantMessage));
if (!m_Initialized)
{
m_Database.DeleteOld();
m_Initialized = true;
}
}
public List<GridInstantMessage> GetMessages(UUID principalID)
{
List<GridInstantMessage> ims = new List<GridInstantMessage>();
OfflineIMData[] messages = m_Database.Get("PrincipalID", principalID.ToString());
if (messages == null || (messages != null && messages.Length == 0))
return ims;
foreach (OfflineIMData m in messages)
{
using (MemoryStream mstream = new MemoryStream(Encoding.UTF8.GetBytes(m.Data["Message"])))
{
GridInstantMessage im = (GridInstantMessage)m_serializer.Deserialize(mstream);
ims.Add(im);
}
}
// Then, delete them
m_Database.Delete("PrincipalID", principalID.ToString());
return ims;
}
public bool StoreMessage(GridInstantMessage im, out string reason)
{
reason = string.Empty;
// Check limits
UUID principalID = new UUID(im.toAgentID);
long count = m_Database.GetCount("PrincipalID", principalID.ToString());
if (count >= MAX_IM)
{
reason = "Number of offline IMs has maxed out";
return false;
}
string imXml;
using (MemoryStream mstream = new MemoryStream())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Util.UTF8NoBomEncoding;
using (XmlWriter writer = XmlWriter.Create(mstream, settings))
{
m_serializer.Serialize(writer, im);
writer.Flush();
imXml = Util.UTF8NoBomEncoding.GetString(mstream.ToArray());
}
}
OfflineIMData data = new OfflineIMData();
data.PrincipalID = principalID;
data.FromID = new UUID(im.fromAgentID);
data.Data = new Dictionary<string, string>();
data.Data["Message"] = imXml;
return m_Database.Store(data);
}
public void DeleteMessages(UUID userID)
{
m_Database.Delete("PrincipalID", userID.ToString());
m_Database.Delete("FromID", userID.ToString());
}
}
}

View File

@ -32,17 +32,16 @@ using System.Threading;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.RegionLoader.Filesystem;
using OpenSim.Framework.RegionLoader.Web;
using OpenSim.Region.CoreModules.Agent.AssetTransaction;
using OpenSim.Region.CoreModules.Avatar.InstantMessage;
using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.CoreModules.Scripting.LoadImageURL;
using OpenSim.Region.CoreModules.Scripting.XMLRPC;
using OpenSim.Services.Interfaces;
using Mono.Addins;
namespace OpenSim.ApplicationPlugins.LoadRegions
{
[Extension(Path="/OpenSim/Startup", Id="LoadRegions", NodeName="Plugin")]
public class LoadRegionsPlugin : IApplicationPlugin, IRegionCreator
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@ -100,12 +99,12 @@ namespace OpenSim.ApplicationPlugins.LoadRegions
RegionInfo[] regionsToLoad = regionLoader.LoadRegions();
m_log.Info("[LOAD REGIONS PLUGIN]: Loading specific shared modules...");
//m_log.Info("[LOAD REGIONS PLUGIN]: DynamicTextureModule...");
//m_openSim.ModuleLoader.LoadDefaultSharedModule(new DynamicTextureModule());
//m_log.Info("[LOAD REGIONS PLUGIN]: LoadImageURLModule...");
//m_openSim.ModuleLoader.LoadDefaultSharedModule(new LoadImageURLModule());
//m_log.Info("[LOAD REGIONS PLUGIN]: XMLRPCModule...");
//m_openSim.ModuleLoader.LoadDefaultSharedModule(new XMLRPCModule());
m_log.Info("[LOAD REGIONS PLUGIN]: DynamicTextureModule...");
m_openSim.ModuleLoader.LoadDefaultSharedModule(new DynamicTextureModule());
m_log.Info("[LOAD REGIONS PLUGIN]: LoadImageURLModule...");
m_openSim.ModuleLoader.LoadDefaultSharedModule(new LoadImageURLModule());
m_log.Info("[LOAD REGIONS PLUGIN]: XMLRPCModule...");
m_openSim.ModuleLoader.LoadDefaultSharedModule(new XMLRPCModule());
// m_log.Info("[LOADREGIONSPLUGIN]: AssetTransactionModule...");
// m_openSim.ModuleLoader.LoadDefaultSharedModule(new AssetTransactionModule());
m_log.Info("[LOAD REGIONS PLUGIN]: Done.");
@ -116,34 +115,29 @@ namespace OpenSim.ApplicationPlugins.LoadRegions
Environment.Exit(1);
}
List<IScene> createdScenes = new List<IScene>();
for (int i = 0; i < regionsToLoad.Length; i++)
{
IScene scene;
m_log.Debug("[LOAD REGIONS PLUGIN]: Creating Region: " + regionsToLoad[i].RegionName + " (ThreadID: " +
Thread.CurrentThread.ManagedThreadId.ToString() +
")");
bool changed = m_openSim.PopulateRegionEstateInfo(regionsToLoad[i]);
m_openSim.PopulateRegionEstateInfo(regionsToLoad[i]);
m_openSim.CreateRegion(regionsToLoad[i], true, out scene);
createdScenes.Add(scene);
if (changed)
m_openSim.EstateDataService.StoreEstateSettings(regionsToLoad[i].EstateSettings);
}
foreach (IScene scene in createdScenes)
{
scene.Start();
m_newRegionCreatedHandler = OnNewRegionCreated;
if (m_newRegionCreatedHandler != null)
regionsToLoad[i].EstateSettings.Save();
if (scene != null)
{
m_newRegionCreatedHandler(scene);
m_newRegionCreatedHandler = OnNewRegionCreated;
if (m_newRegionCreatedHandler != null)
{
m_newRegionCreatedHandler(scene);
}
}
}
m_openSim.ModuleLoader.PostInitialise();
m_openSim.ModuleLoader.ClearCache();
}
public void Dispose()

View File

@ -27,17 +27,16 @@
using System.Reflection;
using System.Runtime.InteropServices;
using Mono.Addins;
// General information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly : AssemblyTitle("OpenSim.ApplicationPlugins.LoadRegions")]
[assembly : AssemblyTitle("OpenSim.Addin")]
[assembly : AssemblyDescription("")]
[assembly : AssemblyConfiguration("")]
[assembly : AssemblyCompany("http://opensimulator.org")]
[assembly : AssemblyProduct("OpenSim")]
[assembly : AssemblyProduct("OpenSim.Addin")]
[assembly : AssemblyCopyright("Copyright © OpenSimulator.org Developers 2007-2009")]
[assembly : AssemblyTrademark("")]
[assembly : AssemblyCulture("")]
@ -61,8 +60,7 @@ using Mono.Addins;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("0.6.5.*")]
[assembly : AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]
[assembly: Addin("OpenSim.ApplicationPlugins.LoadRegions", OpenSim.VersionInfo.VersionNumber)]
[assembly: AddinDependency("OpenSim", OpenSim.VersionInfo.VersionNumber)]
[assembly : AssemblyVersion("0.6.5.*")]
[assembly : AssemblyFileVersion("0.6.5.0")]

View File

@ -1,143 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Xml;
using log4net;
using Nini.Config;
using OpenSim.Framework;
namespace OpenSim.ApplicationPlugins.LoadRegions
{
public class RegionLoaderWebServer : IRegionLoader
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IConfigSource m_configSource;
public void SetIniConfigSource(IConfigSource configSource)
{
m_configSource = configSource;
}
public RegionInfo[] LoadRegions()
{
int tries = 3;
int wait = 2000;
if (m_configSource == null)
{
m_log.Error("[WEBLOADER]: Unable to load configuration source!");
return null;
}
else
{
IConfig startupConfig = (IConfig)m_configSource.Configs["Startup"];
string url = startupConfig.GetString("regionload_webserver_url", String.Empty).Trim();
bool allowRegionless = startupConfig.GetBoolean("allow_regionless", false);
if (url == String.Empty)
{
m_log.Error("[WEBLOADER]: Unable to load webserver URL - URL was empty.");
return null;
}
else
{
while (tries > 0)
{
RegionInfo[] regionInfos = new RegionInfo[] { };
int regionCount = 0;
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Timeout = 30000; //30 Second Timeout
m_log.DebugFormat("[WEBLOADER]: Sending download request to {0}", url);
try
{
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
m_log.Debug("[WEBLOADER]: Downloading region information...");
StreamReader reader = new StreamReader(webResponse.GetResponseStream());
string xmlSource = String.Empty;
string tempStr = reader.ReadLine();
while (tempStr != null)
{
xmlSource = xmlSource + tempStr;
tempStr = reader.ReadLine();
}
m_log.Debug("[WEBLOADER]: Done downloading region information from server. Total Bytes: " +
xmlSource.Length);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlSource);
if (xmlDoc.FirstChild.Name == "Nini")
{
regionCount = xmlDoc.FirstChild.ChildNodes.Count;
if (regionCount > 0)
{
regionInfos = new RegionInfo[regionCount];
int i;
for (i = 0; i < xmlDoc.FirstChild.ChildNodes.Count; i++)
{
m_log.Debug(xmlDoc.FirstChild.ChildNodes[i].OuterXml);
regionInfos[i] =
new RegionInfo("REGION CONFIG #" + (i + 1), xmlDoc.FirstChild.ChildNodes[i], false, m_configSource);
}
}
}
}
catch (WebException ex)
{
if (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.NotFound)
{
if (!allowRegionless)
throw ex;
}
else
throw ex;
}
if (regionCount > 0 || allowRegionless)
return regionInfos;
m_log.Debug("[WEBLOADER]: Request yielded no regions.");
tries--;
if (tries > 0)
{
m_log.Debug("[WEBLOADER]: Retrying");
System.Threading.Thread.Sleep(wait);
}
}
m_log.Error("[WEBLOADER]: No region configs were available.");
return null;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
<Addin id="OpenSim.ApplicationPlugins.LoadRegions" version="0.1">
<Runtime>
<Import assembly="OpenSim.ApplicationPlugins.LoadRegions.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/Startup">
<Plugin id="LoadRegions" type="OpenSim.ApplicationPlugins.LoadRegions.LoadRegionsPlugin" />
</Extension>
</Addin>

View File

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Mono.Addins;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenSim.ApplicationPlugins.RegionModulesController")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("http://opensimulator.org")]
[assembly: AssemblyProduct("OpenSim")]
[assembly: AssemblyCopyright("OpenSimulator developers")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c023816d-194e-40c1-9195-a0f281d4ac5d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]
[assembly: Addin("OpenSim.ApplicationPlugins.RegionModulesController", OpenSim.VersionInfo.VersionNumber)]
[assembly: AddinDependency("OpenSim", OpenSim.VersionInfo.VersionNumber)]

View File

@ -32,13 +32,11 @@ using log4net;
using Mono.Addins;
using Nini.Config;
using OpenSim;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.ApplicationPlugins.RegionModulesController
{
[Extension(Path = "/OpenSim/Startup", Id = "LoadRegions", NodeName = "Plugin")]
public class RegionModulesControllerPlugin : IRegionModulesController,
IApplicationPlugin
{
@ -47,12 +45,6 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Controls whether we load modules from Mono.Addins.
/// </summary>
/// <remarks>For debug purposes. Defaults to true.</remarks>
public bool LoadModulesFromAddins { get; set; }
// Config access
private OpenSimBase m_openSim;
@ -69,22 +61,14 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
private List<ISharedRegionModule> m_sharedInstances =
new List<ISharedRegionModule>();
public RegionModulesControllerPlugin()
{
LoadModulesFromAddins = true;
}
#region IApplicationPlugin implementation
public void Initialise (OpenSimBase openSim)
{
m_openSim = openSim;
m_openSim.ApplicationRegistry.RegisterInterface<IRegionModulesController>(this);
m_log.DebugFormat("[REGIONMODULES]: Initializing...");
if (!LoadModulesFromAddins)
return;
// Who we are
string id = AddinManager.CurrentAddin.Id;
@ -101,20 +85,30 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
if (modulesConfig == null)
modulesConfig = m_openSim.ConfigSource.Source.AddConfig("Modules");
Dictionary<RuntimeAddin, IList<int>> loadedModules = new Dictionary<RuntimeAddin, IList<int>>();
// Scan modules and load all that aren't disabled
foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules"))
AddNode(node, modulesConfig, loadedModules);
foreach (KeyValuePair<RuntimeAddin, IList<int>> loadedModuleData in loadedModules)
foreach (TypeExtensionNode node in
AddinManager.GetExtensionNodes("/OpenSim/RegionModules"))
{
m_log.InfoFormat(
"[REGIONMODULES]: From plugin {0}, (version {1}), loaded {2} modules, {3} shared, {4} non-shared {5} unknown",
loadedModuleData.Key.Id,
loadedModuleData.Key.Version,
loadedModuleData.Value[0] + loadedModuleData.Value[1] + loadedModuleData.Value[2],
loadedModuleData.Value[0], loadedModuleData.Value[1], loadedModuleData.Value[2]);
if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null)
{
if (CheckModuleEnabled(node, modulesConfig))
{
m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type);
m_sharedModules.Add(node);
}
}
else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null)
{
if (CheckModuleEnabled(node, modulesConfig))
{
m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type);
m_nonSharedModules.Add(node);
}
}
else
{
m_log.DebugFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type);
}
}
// Load and init the module. We try a constructor with a port
@ -131,9 +125,6 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
// Read the config again
string moduleString =
modulesConfig.GetString("Setup_" + node.Id, String.Empty);
// Test to see if we want this module
if (moduleString == "disabled")
continue;
// Get the port number, if there is one
if (moduleString != String.Empty)
@ -181,41 +172,6 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
#region IPlugin implementation
private void AddNode(
TypeExtensionNode node, IConfig modulesConfig, Dictionary<RuntimeAddin, IList<int>> loadedModules)
{
IList<int> loadedModuleData;
if (!loadedModules.ContainsKey(node.Addin))
loadedModules.Add(node.Addin, new List<int> { 0, 0, 0 });
loadedModuleData = loadedModules[node.Addin];
if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null)
{
if (CheckModuleEnabled(node, modulesConfig))
{
m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type);
m_sharedModules.Add(node);
loadedModuleData[0]++;
}
}
else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null)
{
if (CheckModuleEnabled(node, modulesConfig))
{
m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type);
m_nonSharedModules.Add(node);
loadedModuleData[1]++;
}
}
else
{
m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type);
loadedModuleData[2]++;
}
}
// We don't do that here
//
public void Initialise ()
@ -237,7 +193,6 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
m_sharedInstances[0].Close();
m_sharedInstances.RemoveAt(0);
}
m_sharedModules.Clear();
m_nonSharedModules.Clear();
}
@ -260,8 +215,8 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
}
}
#region Region Module interfacesController implementation
#region IRegionModulesController implementation
/// <summary>
/// Check that the given module is no disabled in the [Modules] section of the config files.
/// </summary>
@ -293,10 +248,10 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
if (className != String.Empty &&
node.Type.ToString() != className)
return false;
}
}
return true;
}
}
// The root of all evil.
// This is where we handle adding the modules to scenes when they
@ -368,10 +323,6 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
string moduleString =
modulesConfig.GetString("Setup_" + node.Id, String.Empty);
// We may not want to load this at all
if (moduleString == "disabled")
continue;
// Get the port number, if there is one
if (moduleString != String.Empty)
{
@ -509,8 +460,6 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
{
module.RegionLoaded(scene);
}
scene.AllModulesLoaded();
}
public void RemoveRegionFromModules (Scene scene)

View File

@ -0,0 +1,13 @@
<Addin id="OpenSim.ApplicationPlugins.RegionModulesController" version="0.1">
<Runtime>
<Import assembly="OpenSim.ApplicationPlugins.RegionModulesController.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/Startup">
<Plugin id="RegionModulesController" type="OpenSim.ApplicationPlugins.RegionModulesController.RegionModulesControllerPlugin" />
</Extension>
</Addin>

View File

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Mono.Addins;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenSim.ApplicationPlugins.RemoteController")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("http://opensimulator.org")]
[assembly: AssemblyProduct("OpenSim")]
[assembly: AssemblyCopyright("Copyright OpenSimulator developers © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("efec6e69-fc4a-4e21-86e6-4a261c12d4db")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]
[assembly: Addin("OpenSim.ApplicationPlugins.RemoteController", OpenSim.VersionInfo.VersionNumber)]
[assembly: AddinDependency("OpenSim", OpenSim.VersionInfo.VersionNumber)]

View File

@ -0,0 +1,11 @@
<Addin id="OpenSim.ApplicationPlugins.RemoteController" version="0.1">
<Runtime>
<Import assembly="OpenSim.ApplicationPlugins.RemoteController.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/Startup">
<Plugin id="RemoteController" type="OpenSim.ApplicationPlugins.RemoteController.RemoteAdminPlugin" />
</Extension>
</Addin>

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
/// <summary>
/// This interface represents the boundary between the general purpose
/// REST plugin handling, and the functionally specific handlers. The
/// handler knows only to initialize and terminate all such handlers
/// that it finds. Implementing this interface identifies the class as
/// a REST handler implementation.
/// </summary>
internal interface IRest
{
void Initialize();
void Close();
}
}

View File

@ -25,24 +25,35 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using OpenSim.Framework.Servers.HttpServer;
namespace OpenSim.Framework.Console
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
/// <remarks>
/// The handler delegates are not noteworthy. The allocator allows
/// a given handler to optionally subclass the base RequestData
/// structure to carry any locally required per-request state
/// needed.
/// </remarks>
public delegate void RestMethodHandler(RequestData rdata);
public delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response, string path);
/// <summary>
/// This will be a set of typical column sizes to allow greater consistency between console commands.
/// This interface exports the generic plugin-handling services
/// available to each loaded REST services module (IRest implementation)
/// </summary>
public static class ConsoleDisplayUtil
internal interface IRestHandler
{
public const int CoordTupleSize = 11;
public const int PortSize = 5;
public const int EstateNameSize = 20;
public const int ParcelNameSize = 40;
public const int RegionNameSize = 20;
public const int UserNameSize = 35;
string MsgId { get; }
string RequestId { get; }
void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ma);
void AddStreamHandler(string httpMethod, string path, RestMethod method);
public const int UuidSize = 36;
public const int VectorSize = 15;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
<Addin id="OpenSim.ApplicationPlugins.Rest.Inventory" version="0.1">
<Runtime>
<Import assembly="OpenSim.ApplicationPlugins.Rest.Inventory.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/Startup">
<Plugin id="RestInventory" type="OpenSim.ApplicationPlugins.Rest.Inventory.RestHandler" />
</Extension>
</Addin>

View File

@ -0,0 +1,551 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using log4net;
using Nini.Config;
using OpenSim.Framework;
using OpenSim.Framework.Communications;
using OpenSim.Services.Interfaces;
using IAvatarService = OpenSim.Services.Interfaces.IAvatarService;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class Rest
{
internal static readonly ILog Log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
internal static bool DEBUG = Log.IsDebugEnabled;
/// <summary>
/// Supported authentication schemes
/// </summary>
public const string AS_BASIC = "Basic"; // simple user/password verification
public const string AS_DIGEST = "Digest"; // password safe authentication
/// Supported Digest algorithms
public const string Digest_MD5 = "MD5"; // assumed default if omitted
public const string Digest_MD5Sess = "MD5-sess"; // session-span - not good for REST?
public const string Qop_Auth = "auth"; // authentication only
public const string Qop_Int = "auth-int"; // TODO
/// <summary>
/// These values have a single value for the whole
/// domain and lifetime of the plugin handler. We
/// make them static for ease of reference within
/// the assembly. These are initialized by the
/// RestHandler class during start-up.
/// </summary>
internal static IRestHandler Plugin = null;
internal static OpenSimBase main = null;
internal static string Prefix = null;
internal static IConfig Config = null;
internal static string GodKey = null;
internal static bool Authenticate = true;
internal static bool Secure = true;
internal static bool ExtendedEscape = true;
internal static bool DumpAsset = false;
internal static bool Fill = true;
internal static bool FlushEnabled = true;
internal static string Realm = "OpenSim REST";
internal static string Scheme = AS_BASIC;
internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
/// <summary>
/// These are all dependent upon the Comms manager
/// being initialized. So they have to be properties
/// because the comms manager is now a module and is
/// not guaranteed to be there when the rest handler
/// initializes.
/// </summary>
internal static IInventoryService InventoryServices
{
get { return main.SceneManager.CurrentOrFirstScene.InventoryService; }
}
internal static IUserAccountService UserServices
{
get { return main.SceneManager.CurrentOrFirstScene.UserAccountService; }
}
internal static IAuthenticationService AuthServices
{
get { return main.SceneManager.CurrentOrFirstScene.AuthenticationService; }
}
internal static IAvatarService AvatarServices
{
get { return main.SceneManager.CurrentOrFirstScene.AvatarService; }
}
internal static IAssetService AssetServices
{
get { return main.SceneManager.CurrentOrFirstScene.AssetService; }
}
/// <summary>
/// HTTP requires that status information be generated for PUT
/// and POST opertaions. This is in support of that. The
/// operation verb gets substituted into the first string,
/// and the completion code is inserted into the tail. The
/// strings are put here to encourage consistency.
/// </summary>
internal static string statusHead = "<html><body><title>{0} status</title><break>";
internal static string statusTail = "</body></html>";
internal static Dictionary<int,string> HttpStatusDesc;
static Rest()
{
HttpStatusDesc = new Dictionary<int,string>();
if (HttpStatusCodeArray.Length != HttpStatusDescArray.Length)
{
Log.ErrorFormat("{0} HTTP Status Code and Description arrays do not match");
throw new Exception("HTTP Status array discrepancy");
}
// Repackage the data into something more tractable. The sparse
// nature of HTTP return codes makes an array a bad choice.
for (int i=0; i<HttpStatusCodeArray.Length; i++)
{
HttpStatusDesc.Add(HttpStatusCodeArray[i], HttpStatusDescArray[i]);
}
}
internal static int CreationDate
{
get { return (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
}
internal static string MsgId
{
get { return Plugin.MsgId; }
}
internal static string RequestId
{
get { return Plugin.RequestId; }
}
internal static Encoding Encoding = Util.UTF8;
/// <summary>
/// Version control for REST implementation. This
/// refers to the overall infrastructure represented
/// by the following classes
/// RequestData
/// RequestInventoryPlugin
/// Rest
/// It does no describe implementation classes such as
/// RestInventoryServices, which may morph much more
/// often. Such classes ARE dependent upon this however
/// and should check it in their Initialize method.
/// </summary>
public static readonly float Version = 1.0F;
public const string Name = "REST 1.0";
/// <summary>
/// Currently defined HTTP methods.
/// Only GET and HEAD are required to be
/// supported by all servers. See Respond
/// to see how these are handled.
/// </summary>
// REST AGENT 1.0 interpretations
public const string GET = "get"; // information retrieval - server state unchanged
public const string HEAD = "head"; // same as get except only the headers are returned.
public const string POST = "post"; // Replace the URI designated resource with the entity.
public const string PUT = "put"; // Add the entity to the context represented by the URI
public const string DELETE = "delete"; // Remove the URI designated resource from the server.
public const string OPTIONS = "options"; //
public const string TRACE = "trace"; //
public const string CONNECT = "connect"; //
// Define this in one place...
public const string UrlPathSeparator = "/";
public const string UrlMethodSeparator = ":";
// Redirection qualifications
public const bool PERMANENT = false;
public const bool TEMPORARY = true;
// Constant arrays used by String.Split
public static readonly char C_SPACE = ' ';
public static readonly char C_SLASH = '/';
public static readonly char C_PATHSEP = '/';
public static readonly char C_COLON = ':';
public static readonly char C_PLUS = '+';
public static readonly char C_PERIOD = '.';
public static readonly char C_COMMA = ',';
public static readonly char C_DQUOTE = '"';
public static readonly string CS_SPACE = " ";
public static readonly string CS_SLASH = "/";
public static readonly string CS_PATHSEP = "/";
public static readonly string CS_COLON = ":";
public static readonly string CS_PLUS = "+";
public static readonly string CS_PERIOD = ".";
public static readonly string CS_COMMA = ",";
public static readonly string CS_DQUOTE = "\"";
public static readonly char[] CA_SPACE = { C_SPACE };
public static readonly char[] CA_SLASH = { C_SLASH };
public static readonly char[] CA_PATHSEP = { C_PATHSEP };
public static readonly char[] CA_COLON = { C_COLON };
public static readonly char[] CA_PERIOD = { C_PERIOD };
public static readonly char[] CA_PLUS = { C_PLUS };
public static readonly char[] CA_COMMA = { C_COMMA };
public static readonly char[] CA_DQUOTE = { C_DQUOTE };
// HTTP Code Values (in value order)
public const int HttpStatusCodeContinue = 100;
public const int HttpStatusCodeSwitchingProtocols = 101;
public const int HttpStatusCodeOK = 200;
public const int HttpStatusCodeCreated = 201;
public const int HttpStatusCodeAccepted = 202;
public const int HttpStatusCodeNonAuthoritative = 203;
public const int HttpStatusCodeNoContent = 204;
public const int HttpStatusCodeResetContent = 205;
public const int HttpStatusCodePartialContent = 206;
public const int HttpStatusCodeMultipleChoices = 300;
public const int HttpStatusCodePermanentRedirect = 301;
public const int HttpStatusCodeFound = 302;
public const int HttpStatusCodeSeeOther = 303;
public const int HttpStatusCodeNotModified = 304;
public const int HttpStatusCodeUseProxy = 305;
public const int HttpStatusCodeReserved306 = 306;
public const int HttpStatusCodeTemporaryRedirect = 307;
public const int HttpStatusCodeBadRequest = 400;
public const int HttpStatusCodeNotAuthorized = 401;
public const int HttpStatusCodePaymentRequired = 402;
public const int HttpStatusCodeForbidden = 403;
public const int HttpStatusCodeNotFound = 404;
public const int HttpStatusCodeMethodNotAllowed = 405;
public const int HttpStatusCodeNotAcceptable = 406;
public const int HttpStatusCodeProxyAuthenticate = 407;
public const int HttpStatusCodeTimeOut = 408;
public const int HttpStatusCodeConflict = 409;
public const int HttpStatusCodeGone = 410;
public const int HttpStatusCodeLengthRequired = 411;
public const int HttpStatusCodePreconditionFailed = 412;
public const int HttpStatusCodeEntityTooLarge = 413;
public const int HttpStatusCodeUriTooLarge = 414;
public const int HttpStatusCodeUnsupportedMedia = 415;
public const int HttpStatusCodeRangeNotSatsified = 416;
public const int HttpStatusCodeExpectationFailed = 417;
public const int HttpStatusCodeServerError = 500;
public const int HttpStatusCodeNotImplemented = 501;
public const int HttpStatusCodeBadGateway = 502;
public const int HttpStatusCodeServiceUnavailable = 503;
public const int HttpStatusCodeGatewayTimeout = 504;
public const int HttpStatusCodeHttpVersionError = 505;
public static readonly int[] HttpStatusCodeArray = {
HttpStatusCodeContinue,
HttpStatusCodeSwitchingProtocols,
HttpStatusCodeOK,
HttpStatusCodeCreated,
HttpStatusCodeAccepted,
HttpStatusCodeNonAuthoritative,
HttpStatusCodeNoContent,
HttpStatusCodeResetContent,
HttpStatusCodePartialContent,
HttpStatusCodeMultipleChoices,
HttpStatusCodePermanentRedirect,
HttpStatusCodeFound,
HttpStatusCodeSeeOther,
HttpStatusCodeNotModified,
HttpStatusCodeUseProxy,
HttpStatusCodeReserved306,
HttpStatusCodeTemporaryRedirect,
HttpStatusCodeBadRequest,
HttpStatusCodeNotAuthorized,
HttpStatusCodePaymentRequired,
HttpStatusCodeForbidden,
HttpStatusCodeNotFound,
HttpStatusCodeMethodNotAllowed,
HttpStatusCodeNotAcceptable,
HttpStatusCodeProxyAuthenticate,
HttpStatusCodeTimeOut,
HttpStatusCodeConflict,
HttpStatusCodeGone,
HttpStatusCodeLengthRequired,
HttpStatusCodePreconditionFailed,
HttpStatusCodeEntityTooLarge,
HttpStatusCodeUriTooLarge,
HttpStatusCodeUnsupportedMedia,
HttpStatusCodeRangeNotSatsified,
HttpStatusCodeExpectationFailed,
HttpStatusCodeServerError,
HttpStatusCodeNotImplemented,
HttpStatusCodeBadGateway,
HttpStatusCodeServiceUnavailable,
HttpStatusCodeGatewayTimeout,
HttpStatusCodeHttpVersionError
};
// HTTP Status Descriptions (in status code order)
// This array must be kept strictly consistent with respect
// to the status code array above.
public static readonly string[] HttpStatusDescArray = {
"Continue Request",
"Switching Protocols",
"OK",
"CREATED",
"ACCEPTED",
"NON-AUTHORITATIVE INFORMATION",
"NO CONTENT",
"RESET CONTENT",
"PARTIAL CONTENT",
"MULTIPLE CHOICES",
"PERMANENT REDIRECT",
"FOUND",
"SEE OTHER",
"NOT MODIFIED",
"USE PROXY",
"RESERVED CODE 306",
"TEMPORARY REDIRECT",
"BAD REQUEST",
"NOT AUTHORIZED",
"PAYMENT REQUIRED",
"FORBIDDEN",
"NOT FOUND",
"METHOD NOT ALLOWED",
"NOT ACCEPTABLE",
"PROXY AUTHENTICATION REQUIRED",
"TIMEOUT",
"CONFLICT",
"GONE",
"LENGTH REQUIRED",
"PRECONDITION FAILED",
"ENTITY TOO LARGE",
"URI TOO LARGE",
"UNSUPPORTED MEDIA",
"RANGE NOT SATISFIED",
"EXPECTATION FAILED",
"SERVER ERROR",
"NOT IMPLEMENTED",
"BAD GATEWAY",
"SERVICE UNAVAILABLE",
"GATEWAY TIMEOUT",
"HTTP VERSION NOT SUPPORTED"
};
// HTTP Headers
public const string HttpHeaderAccept = "Accept";
public const string HttpHeaderAcceptCharset = "Accept-Charset";
public const string HttpHeaderAcceptEncoding = "Accept-Encoding";
public const string HttpHeaderAcceptLanguage = "Accept-Language";
public const string HttpHeaderAcceptRanges = "Accept-Ranges";
public const string HttpHeaderAge = "Age";
public const string HttpHeaderAllow = "Allow";
public const string HttpHeaderAuthorization = "Authorization";
public const string HttpHeaderCacheControl = "Cache-Control";
public const string HttpHeaderConnection = "Connection";
public const string HttpHeaderContentEncoding = "Content-Encoding";
public const string HttpHeaderContentLanguage = "Content-Language";
public const string HttpHeaderContentLength = "Content-Length";
public const string HttpHeaderContentLocation = "Content-Location";
public const string HttpHeaderContentMD5 = "Content-MD5";
public const string HttpHeaderContentRange = "Content-Range";
public const string HttpHeaderContentType = "Content-Type";
public const string HttpHeaderDate = "Date";
public const string HttpHeaderETag = "ETag";
public const string HttpHeaderExpect = "Expect";
public const string HttpHeaderExpires = "Expires";
public const string HttpHeaderFrom = "From";
public const string HttpHeaderHost = "Host";
public const string HttpHeaderIfMatch = "If-Match";
public const string HttpHeaderIfModifiedSince = "If-Modified-Since";
public const string HttpHeaderIfNoneMatch = "If-None-Match";
public const string HttpHeaderIfRange = "If-Range";
public const string HttpHeaderIfUnmodifiedSince = "If-Unmodified-Since";
public const string HttpHeaderLastModified = "Last-Modified";
public const string HttpHeaderLocation = "Location";
public const string HttpHeaderMaxForwards = "Max-Forwards";
public const string HttpHeaderPragma = "Pragma";
public const string HttpHeaderProxyAuthenticate = "Proxy-Authenticate";
public const string HttpHeaderProxyAuthorization = "Proxy-Authorization";
public const string HttpHeaderRange = "Range";
public const string HttpHeaderReferer = "Referer";
public const string HttpHeaderRetryAfter = "Retry-After";
public const string HttpHeaderServer = "Server";
public const string HttpHeaderTE = "TE";
public const string HttpHeaderTrailer = "Trailer";
public const string HttpHeaderTransferEncoding = "Transfer-Encoding";
public const string HttpHeaderUpgrade = "Upgrade";
public const string HttpHeaderUserAgent = "User-Agent";
public const string HttpHeaderVary = "Vary";
public const string HttpHeaderVia = "Via";
public const string HttpHeaderWarning = "Warning";
public const string HttpHeaderWWWAuthenticate = "WWW-Authenticate";
/// Utility routines
public static string StringToBase64(string str)
{
try
{
byte[] encData_byte = new byte[str.Length];
encData_byte = Util.UTF8.GetBytes(str);
return Convert.ToBase64String(encData_byte);
}
catch
{
return String.Empty;
}
}
public static string Base64ToString(string str)
{
try
{
return Util.Base64ToString(str);
}
catch
{
return String.Empty;
}
}
private const string hvals = "0123456789abcdef";
public static int Hex2Int(string hex)
{
int val = 0;
int sum = 0;
string tmp = null;
if (hex != null)
{
tmp = hex.ToLower();
for (int i = 0; i < tmp.Length; i++)
{
val = hvals.IndexOf(tmp[i]);
if (val == -1)
break;
sum *= 16;
sum += val;
}
}
return sum;
}
// Nonce management
public static string NonceGenerator()
{
return StringToBase64(CreationDate + Guid.NewGuid().ToString());
}
// Dump the specified data stream
public static void Dump(byte[] data)
{
char[] buffer = new char[DumpLineSize];
int cc = 0;
for (int i = 0; i < data.Length; i++)
{
if (i % DumpLineSize == 0) Console.Write("\n{0}: ",i.ToString("d8"));
if (i % 4 == 0) Console.Write(" ");
Console.Write("{0}",data[i].ToString("x2"));
if (data[i] < 127 && data[i] > 31)
buffer[i % DumpLineSize] = (char) data[i];
else
buffer[i % DumpLineSize] = '.';
cc++;
if (i != 0 && (i + 1) % DumpLineSize == 0)
{
Console.Write(" |"+(new String(buffer))+"|");
cc = 0;
}
}
// Finish off any incomplete line
if (cc != 0)
{
for (int i = cc ; i < DumpLineSize; i++)
{
if (i % 4 == 0) Console.Write(" ");
Console.Write(" ");
buffer[i % DumpLineSize] = ' ';
}
Console.WriteLine(" |"+(new String(buffer))+"|");
}
else
{
Console.Write("\n");
}
}
}
// Local exception type
public class RestException : Exception
{
internal int statusCode;
internal string statusDesc;
internal string httpmethod;
internal string httppath;
public RestException(string msg) : base(msg)
{
}
}
}

View File

@ -0,0 +1,860 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Services.Interfaces;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class RestAppearanceServices : IRest
{
// private static readonly int PARM_USERID = 0;
// private static readonly int PARM_PATH = 1;
// private bool enabled = false;
private string qPrefix = "appearance";
/// <summary>
/// The constructor makes sure that the service prefix is absolute
/// and the registers the service handler and the allocator.
/// </summary>
public RestAppearanceServices()
{
Rest.Log.InfoFormat("{0} User appearance services initializing", MsgId);
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
// If a relative path was specified for the handler's domain,
// add the standard prefix to make it absolute, e.g. /admin
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
{
Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
}
// Register interface using the absolute URI.
Rest.Plugin.AddPathHandler(DoAppearance,qPrefix,Allocate);
// Activate if everything went OK
// enabled = true;
Rest.Log.InfoFormat("{0} User appearance services initialization complete", MsgId);
}
/// <summary>
/// Post-construction, pre-enabled initialization opportunity
/// Not currently exploited.
/// </summary>
public void Initialize()
{
}
/// <summary>
/// Called by the plug-in to halt service processing. Local processing is
/// disabled.
/// </summary>
public void Close()
{
// enabled = false;
Rest.Log.InfoFormat("{0} User appearance services closing down", MsgId);
}
/// <summary>
/// This property is declared locally because it is used a lot and
/// brevity is nice.
/// </summary>
internal string MsgId
{
get { return Rest.MsgId; }
}
#region Interface
/// <summary>
/// The plugin (RestHandler) calls this method to allocate the request
/// state carrier for a new request. It is destroyed when the request
/// completes. All request-instance specific state is kept here. This
/// is registered when this service provider is registered.
/// </summary>
/// <param name=request>Inbound HTTP request information</param>
/// <param name=response>Outbound HTTP request information</param>
/// <param name=qPrefix>REST service domain prefix</param>
/// <returns>A RequestData instance suitable for this service</returns>
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
{
return (RequestData) new AppearanceRequestData(request, response, prefix);
}
/// <summary>
/// This method is registered with the handler when this service provider
/// is initialized. It is called whenever the plug-in identifies this service
/// provider as the best match for a given request.
/// It handles all aspects of inventory REST processing, i.e. /admin/inventory
/// </summary>
/// <param name=hdata>A consolidated HTTP request work area</param>
private void DoAppearance(RequestData hdata)
{
// !!! REFACTORIMG PROBLEM. This needs rewriting for 0.7
//AppearanceRequestData rdata = (AppearanceRequestData) hdata;
//Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId);
//// If we're disabled, do nothing.
//if (!enabled)
//{
// return;
//}
//// Now that we know this is a serious attempt to
//// access inventory data, we should find out who
//// is asking, and make sure they are authorized
//// to do so. We need to validate the caller's
//// identity before revealing anything about the
//// status quo. Authenticate throws an exception
//// via Fail if no identity information is present.
////
//// With the present HTTP server we can't use the
//// builtin authentication mechanisms because they
//// would be enforced for all in-bound requests.
//// Instead we look at the headers ourselves and
//// handle authentication directly.
//try
//{
// if (!rdata.IsAuthenticated)
// {
// rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
// }
//}
//catch (RestException e)
//{
// if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
// {
// Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
// Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
// }
// else
// {
// Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
// Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
// }
// throw (e);
//}
//Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
//// We can only get here if we are authorized
////
//// The requestor may have specified an UUID or
//// a conjoined FirstName LastName string. We'll
//// try both. If we fail with the first, UUID,
//// attempt, we try the other. As an example, the
//// URI for a valid inventory request might be:
////
//// http://<host>:<port>/admin/inventory/Arthur Dent
////
//// Indicating that this is an inventory request for
//// an avatar named Arthur Dent. This is ALL that is
//// required to designate a GET for an entire
//// inventory.
////
//// Do we have at least a user agent name?
//if (rdata.Parameters.Length < 1)
//{
// Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId);
// rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
//}
//// The first parameter MUST be the agent identification, either an UUID
//// or a space-separated First-name Last-Name specification. We check for
//// an UUID first, if anyone names their character using a valid UUID
//// that identifies another existing avatar will cause this a problem...
//try
//{
// rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
// Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
// rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
//}
//catch
//{
// string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
// if (names.Length == 2)
// {
// Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
// rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]);
// }
// else
// {
// Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
// rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
// }
//}
//// If the user profile is null then either the server is broken, or the
//// user is not known. We always assume the latter case.
//if (rdata.userProfile != null)
//{
// Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}",
// MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
//}
//else
//{
// Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path);
// rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
//}
//// If we get to here, then we have effectively validated the user's
//switch (rdata.method)
//{
// case Rest.HEAD : // Do the processing, set the status code, suppress entity
// DoGet(rdata);
// rdata.buffer = null;
// break;
// case Rest.GET : // Do the processing, set the status code, return entity
// DoGet(rdata);
// break;
// case Rest.PUT : // Update named element
// DoUpdate(rdata);
// break;
// case Rest.POST : // Add new information to identified context.
// DoExtend(rdata);
// break;
// case Rest.DELETE : // Delete information
// DoDelete(rdata);
// break;
// default :
// Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
// MsgId, rdata.method, rdata.path);
// rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
// String.Format("{0} not supported", rdata.method));
// break;
//}
}
#endregion Interface
#region method-specific processing
/// <summary>
/// This method implements GET processing for user's appearance.
/// </summary>
/// <param name=rdata>HTTP service request work area</param>
// private void DoGet(AppearanceRequestData rdata)
// {
// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID);
//
// if (adata == null)
// {
// rdata.Fail(Rest.HttpStatusCodeNoContent,
// String.Format("appearance data not found for user {0} {1}",
// rdata.userProfile.FirstName, rdata.userProfile.SurName));
// }
// rdata.userAppearance = adata.ToAvatarAppearance(rdata.userProfile.ID);
//
// rdata.initXmlWriter();
//
// FormatUserAppearance(rdata);
//
// // Indicate a successful request
//
// rdata.Complete();
//
// // Send the response to the user. The body will be implicitly
// // constructed from the result of the XML writer.
//
// rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
// }
/// <summary>
/// POST adds NEW information to the user profile database.
/// This effectively resets the appearance before applying those
/// characteristics supplied in the request.
/// </summary>
// private void DoExtend(AppearanceRequestData rdata)
// {
//
// bool created = false;
// bool modified = false;
// string newnode = String.Empty;
//
// Rest.Log.DebugFormat("{0} POST ENTRY", MsgId);
//
// //AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
//
// rdata.userAppearance = new AvatarAppearance();
//
// // Although the following behavior is admitted by HTTP I am becoming
// // increasingly doubtful that it is appropriate for REST. If I attempt to
// // add a new record, and it already exists, then it seems to me that the
// // attempt should fail, rather than update the existing record.
// AvatarData adata = null;
// if (GetUserAppearance(rdata))
// {
// modified = rdata.userAppearance != null;
// created = !modified;
// adata = new AvatarData(rdata.userAppearance);
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
// }
// else
// {
// created = true;
// adata = new AvatarData(rdata.userAppearance);
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
// }
//
// if (created)
// {
// newnode = String.Format("{0} {1}", rdata.userProfile.FirstName,
// rdata.userProfile.SurName);
// // Must include a location header with a URI that identifies the new resource.
//
// rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}",
// rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode));
// rdata.Complete(Rest.HttpStatusCodeCreated);
//
// }
// else
// {
// if (modified)
// {
// rdata.Complete(Rest.HttpStatusCodeOK);
// }
// else
// {
// rdata.Complete(Rest.HttpStatusCodeNoContent);
// }
// }
//
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
//
// }
/// <summary>
/// This updates the user's appearance. not all aspects need to be provided,
/// only those supplied will be changed.
/// </summary>
// private void DoUpdate(AppearanceRequestData rdata)
// {
//
// // REFACTORING PROBLEM This was commented out. It doesn't work for 0.7
//
// //bool created = false;
// //bool modified = false;
//
//
// //rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
//
// //// If the user exists then this is considered a modification regardless
// //// of what may, or may not be, specified in the payload.
//
// //if (rdata.userAppearance != null)
// //{
// // modified = true;
// // Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
// //}
//
// //if (created)
// //{
// // rdata.Complete(Rest.HttpStatusCodeCreated);
// //}
// //else
// //{
// // if (modified)
// // {
// // rdata.Complete(Rest.HttpStatusCodeOK);
// // }
// // else
// // {
// // rdata.Complete(Rest.HttpStatusCodeNoContent);
// // }
// //}
//
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
//
// }
/// <summary>
/// Delete the specified user's appearance. This actually performs a reset
/// to the default avatar appearance, if the info is already there.
/// Existing ownership is preserved. All prior updates are lost and can not
/// be recovered.
/// </summary>
// private void DoDelete(AppearanceRequestData rdata)
// {
// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID);
//
// if (adata != null)
// {
// AvatarAppearance old = adata.ToAvatarAppearance(rdata.userProfile.ID);
// rdata.userAppearance = new AvatarAppearance();
// rdata.userAppearance.Owner = old.Owner;
// adata = new AvatarData(rdata.userAppearance);
//
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
//
// rdata.Complete();
// }
// else
// {
//
// rdata.Complete(Rest.HttpStatusCodeNoContent);
// }
//
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
// }
#endregion method-specific processing
private bool GetUserAppearance(AppearanceRequestData rdata)
{
XmlReader xml;
bool indata = false;
rdata.initXmlReader();
xml = rdata.reader;
while (xml.Read())
{
switch (xml.NodeType)
{
case XmlNodeType.Element :
switch (xml.Name)
{
case "Appearance" :
if (xml.MoveToAttribute("Height"))
{
rdata.userAppearance.AvatarHeight = (float) Convert.ToDouble(xml.Value);
indata = true;
}
// if (xml.MoveToAttribute("Owner"))
// {
// rdata.userAppearance.Owner = (UUID)xml.Value;
// indata = true;
// }
if (xml.MoveToAttribute("Serial"))
{
rdata.userAppearance.Serial = Convert.ToInt32(xml.Value);
indata = true;
}
break;
/*
case "Body" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.BodyItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.BodyAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Skin" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.SkinItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.SkinAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Hair" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.HairItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.HairAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Eyes" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.EyesItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.EyesAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Shirt" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.ShirtItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.ShirtAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Pants" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.PantsItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.PantsAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Shoes" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.ShoesItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.ShoesAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Socks" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.SocksItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.SocksAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Jacket" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.JacketItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.JacketAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Gloves" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.GlovesItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.GlovesAsset = (UUID)xml.Value;
indata = true;
}
break;
case "UnderShirt" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.UnderShirtItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.UnderShirtAsset = (UUID)xml.Value;
indata = true;
}
break;
case "UnderPants" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.UnderPantsItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.UnderPantsAsset = (UUID)xml.Value;
indata = true;
}
break;
case "Skirt" :
if (xml.MoveToAttribute("Item"))
{
rdata.userAppearance.SkirtItem = (UUID)xml.Value;
indata = true;
}
if (xml.MoveToAttribute("Asset"))
{
rdata.userAppearance.SkirtAsset = (UUID)xml.Value;
indata = true;
}
break;
*/
case "Attachment" :
{
int ap;
UUID asset;
UUID item;
if (xml.MoveToAttribute("AtPoint"))
{
ap = Convert.ToInt32(xml.Value);
if (xml.MoveToAttribute("Asset"))
{
asset = new UUID(xml.Value);
if (xml.MoveToAttribute("Asset"))
{
item = new UUID(xml.Value);
rdata.userAppearance.SetAttachment(ap, item, asset);
indata = true;
}
}
}
}
break;
case "Texture" :
if (xml.MoveToAttribute("Default"))
{
rdata.userAppearance.Texture = new Primitive.TextureEntry(new UUID(xml.Value));
indata = true;
}
break;
case "Face" :
{
uint index;
if (xml.MoveToAttribute("Index"))
{
index = Convert.ToUInt32(xml.Value);
if (xml.MoveToAttribute("Id"))
{
rdata.userAppearance.Texture.CreateFace(index).TextureID = new UUID(xml.Value);
indata = true;
}
}
}
break;
case "VisualParameters" :
{
xml.ReadContentAsBase64(rdata.userAppearance.VisualParams,
0, rdata.userAppearance.VisualParams.Length);
indata = true;
}
break;
}
break;
}
}
return indata;
}
private void FormatPart(AppearanceRequestData rdata, string part, UUID item, UUID asset)
{
if (item != UUID.Zero || asset != UUID.Zero)
{
rdata.writer.WriteStartElement(part);
if (item != UUID.Zero)
{
rdata.writer.WriteAttributeString("Item",item.ToString());
}
if (asset != UUID.Zero)
{
rdata.writer.WriteAttributeString("Asset",asset.ToString());
}
rdata.writer.WriteEndElement();
}
}
private void FormatUserAppearance(AppearanceRequestData rdata)
{
Rest.Log.DebugFormat("{0} FormatUserAppearance", MsgId);
if (rdata.userAppearance != null)
{
Rest.Log.DebugFormat("{0} FormatUserAppearance: appearance object exists", MsgId);
rdata.writer.WriteStartElement("Appearance");
rdata.writer.WriteAttributeString("Height", rdata.userAppearance.AvatarHeight.ToString());
// if (rdata.userAppearance.Owner != UUID.Zero)
// rdata.writer.WriteAttributeString("Owner", rdata.userAppearance.Owner.ToString());
rdata.writer.WriteAttributeString("Serial", rdata.userAppearance.Serial.ToString());
/*
FormatPart(rdata, "Body", rdata.userAppearance.BodyItem, rdata.userAppearance.BodyAsset);
FormatPart(rdata, "Skin", rdata.userAppearance.SkinItem, rdata.userAppearance.SkinAsset);
FormatPart(rdata, "Hair", rdata.userAppearance.HairItem, rdata.userAppearance.HairAsset);
FormatPart(rdata, "Eyes", rdata.userAppearance.EyesItem, rdata.userAppearance.EyesAsset);
FormatPart(rdata, "Shirt", rdata.userAppearance.ShirtItem, rdata.userAppearance.ShirtAsset);
FormatPart(rdata, "Pants", rdata.userAppearance.PantsItem, rdata.userAppearance.PantsAsset);
FormatPart(rdata, "Skirt", rdata.userAppearance.SkirtItem, rdata.userAppearance.SkirtAsset);
FormatPart(rdata, "Shoes", rdata.userAppearance.ShoesItem, rdata.userAppearance.ShoesAsset);
FormatPart(rdata, "Socks", rdata.userAppearance.SocksItem, rdata.userAppearance.SocksAsset);
FormatPart(rdata, "Jacket", rdata.userAppearance.JacketItem, rdata.userAppearance.JacketAsset);
FormatPart(rdata, "Gloves", rdata.userAppearance.GlovesItem, rdata.userAppearance.GlovesAsset);
FormatPart(rdata, "UnderShirt", rdata.userAppearance.UnderShirtItem, rdata.userAppearance.UnderShirtAsset);
FormatPart(rdata, "UnderPants", rdata.userAppearance.UnderPantsItem, rdata.userAppearance.UnderPantsAsset);
*/
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting attachments", MsgId);
rdata.writer.WriteStartElement("Attachments");
List<AvatarAttachment> attachments = rdata.userAppearance.GetAttachments();
foreach (AvatarAttachment attach in attachments)
{
rdata.writer.WriteStartElement("Attachment");
rdata.writer.WriteAttributeString("AtPoint", attach.AttachPoint.ToString());
rdata.writer.WriteAttributeString("Item", attach.ItemID.ToString());
rdata.writer.WriteAttributeString("Asset", attach.AssetID.ToString());
rdata.writer.WriteEndElement();
}
rdata.writer.WriteEndElement();
Primitive.TextureEntry texture = rdata.userAppearance.Texture;
if (texture != null && (texture.DefaultTexture != null || texture.FaceTextures != null))
{
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting textures", MsgId);
rdata.writer.WriteStartElement("Texture");
if (texture.DefaultTexture != null)
{
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting default texture", MsgId);
rdata.writer.WriteAttributeString("Default",
texture.DefaultTexture.TextureID.ToString());
}
if (texture.FaceTextures != null)
{
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting face textures", MsgId);
for (int i=0; i<texture.FaceTextures.Length;i++)
{
if (texture.FaceTextures[i] != null)
{
rdata.writer.WriteStartElement("Face");
rdata.writer.WriteAttributeString("Index", i.ToString());
rdata.writer.WriteAttributeString("Id",
texture.FaceTextures[i].TextureID.ToString());
rdata.writer.WriteEndElement();
}
}
}
rdata.writer.WriteEndElement();
}
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting visual parameters", MsgId);
rdata.writer.WriteStartElement("VisualParameters");
rdata.writer.WriteBase64(rdata.userAppearance.VisualParams,0,
rdata.userAppearance.VisualParams.Length);
rdata.writer.WriteEndElement();
rdata.writer.WriteFullEndElement();
}
Rest.Log.DebugFormat("{0} FormatUserAppearance: completed", MsgId);
return;
}
#region appearance RequestData extension
internal class AppearanceRequestData : RequestData
{
/// <summary>
/// These are the inventory specific request/response state
/// extensions.
/// </summary>
internal UUID uuid = UUID.Zero;
internal UserProfileData userProfile = null;
internal AvatarAppearance userAppearance = null;
internal AppearanceRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
: base(request, response, prefix)
{
}
}
#endregion Appearance RequestData extension
}
}

View File

@ -0,0 +1,383 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Xml;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class RestAssetServices : IRest
{
private bool enabled = false;
private string qPrefix = "assets";
// A simple constructor is used to handle any once-only
// initialization of working classes.
public RestAssetServices()
{
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
// If the handler specifies a relative path for its domain
// then we must add the standard absolute prefix, e.g. /admin
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
{
Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix);
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix);
}
// Register interface using the fully-qualified prefix
Rest.Plugin.AddPathHandler(DoAsset, qPrefix, Allocate);
// Activate if all went OK
enabled = true;
Rest.Log.InfoFormat("{0} Asset services initialization complete", MsgId);
}
// Post-construction, pre-enabled initialization opportunity
// Not currently exploited.
public void Initialize()
{
}
// Called by the plug-in to halt REST processing. Local processing is
// disabled, and control blocks until all current processing has
// completed. No new processing will be started
public void Close()
{
enabled = false;
Rest.Log.InfoFormat("{0} Asset services ({1}) closing down", MsgId, qPrefix);
}
// Properties
internal string MsgId
{
get { return Rest.MsgId; }
}
#region Interface
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
{
return (RequestData) new AssetRequestData(request, response, prefix);
}
// Asset Handler
private void DoAsset(RequestData rparm)
{
if (!enabled) return;
AssetRequestData rdata = (AssetRequestData) rparm;
Rest.Log.DebugFormat("{0} REST Asset handler ({1}) ENTRY", MsgId, qPrefix);
// Now that we know this is a serious attempt to
// access inventory data, we should find out who
// is asking, and make sure they are authorized
// to do so. We need to validate the caller's
// identity before revealing anything about the
// status quo. Authenticate throws an exception
// via Fail if no identity information is present.
//
// With the present HTTP server we can't use the
// builtin authentication mechanisms because they
// would be enforced for all in-bound requests.
// Instead we look at the headers ourselves and
// handle authentication directly.
try
{
if (!rdata.IsAuthenticated)
{
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
}
}
catch (RestException e)
{
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
{
Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
rdata.request.Headers.Get("Authorization"));
}
else
{
Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
rdata.request.Headers.Get("Authorization"));
}
throw (e);
}
// Remove the prefix and what's left are the parameters. If we don't have
// the parameters we need, fail the request. Parameters do NOT include
// any supplied query values.
if (rdata.Parameters.Length > 0)
{
switch (rdata.method)
{
case "get" :
DoGet(rdata);
break;
case "put" :
DoPut(rdata);
break;
case "post" :
DoPost(rdata);
break;
case "delete" :
default :
Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}",
MsgId, rdata.method);
rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
break;
}
}
else
{
Rest.Log.WarnFormat("{0} Asset: No agent information provided", MsgId);
rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
}
Rest.Log.DebugFormat("{0} REST Asset handler EXIT", MsgId);
}
#endregion Interface
/// <summary>
/// The only parameter we recognize is a UUID.If an asset with this identification is
/// found, it's content, base-64 encoded, is returned to the client.
/// </summary>
private void DoGet(AssetRequestData rdata)
{
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
if (rdata.Parameters.Length == 1)
{
UUID uuid = new UUID(rdata.Parameters[0]);
AssetBase asset = Rest.AssetServices.Get(uuid.ToString());
if (asset != null)
{
Rest.Log.DebugFormat("{0} Asset located <{1}>", MsgId, rdata.Parameters[0]);
rdata.initXmlWriter();
rdata.writer.WriteStartElement(String.Empty,"Asset",String.Empty);
rdata.writer.WriteAttributeString("id", asset.ID);
rdata.writer.WriteAttributeString("name", asset.Name);
rdata.writer.WriteAttributeString("desc", asset.Description);
rdata.writer.WriteAttributeString("type", asset.Type.ToString());
rdata.writer.WriteAttributeString("local", asset.Local.ToString());
rdata.writer.WriteAttributeString("temporary", asset.Temporary.ToString());
rdata.writer.WriteBase64(asset.Data,0,asset.Data.Length);
rdata.writer.WriteFullEndElement();
}
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
}
}
rdata.Complete();
rdata.Respond(String.Format("Asset <{0}> : Normal completion", rdata.method));
}
/// <summary>
/// UPDATE existing item, if it exists. URI identifies the item in question.
/// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded)
/// is decoded and stored in the database, identified by the supplied UUID.
/// </summary>
private void DoPut(AssetRequestData rdata)
{
bool modified = false;
bool created = false;
AssetBase asset = null;
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
if (rdata.Parameters.Length == 1)
{
rdata.initXmlReader();
XmlReader xml = rdata.reader;
if (!xml.ReadToFollowing("Asset"))
{
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
}
UUID uuid = new UUID(rdata.Parameters[0]);
asset = Rest.AssetServices.Get(uuid.ToString());
modified = (asset != null);
created = !modified;
asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString());
asset.Description = xml.GetAttribute("desc");
asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0;
asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0;
asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", ""));
if (asset.ID != rdata.Parameters[0])
{
Rest.Log.WarnFormat("{0} URI and payload disagree on UUID U:{1} vs P:{2}",
MsgId, rdata.Parameters[0], asset.ID);
}
Rest.AssetServices.Store(asset);
}
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
}
if (created)
{
rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
rdata.Complete(Rest.HttpStatusCodeCreated);
}
else
{
if (modified)
{
rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
rdata.Complete(Rest.HttpStatusCodeOK);
}
else
{
rdata.Complete(Rest.HttpStatusCodeNoContent);
}
}
rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
}
/// <summary>
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
/// No parameters are required for POST, just thepayload.
/// </summary>
private void DoPost(AssetRequestData rdata)
{
bool modified = false;
bool created = false;
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
if (rdata.Parameters.Length != 0)
{
Rest.Log.WarnFormat("{0} Parameters ignored <{1}>", MsgId, rdata.path);
Rest.Log.InfoFormat("{0} POST of an asset has no parameters", MsgId, rdata.path);
}
rdata.initXmlReader();
XmlReader xml = rdata.reader;
if (!xml.ReadToFollowing("Asset"))
{
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
}
UUID uuid = new UUID(xml.GetAttribute("id"));
AssetBase asset = Rest.AssetServices.Get(uuid.ToString());
modified = (asset != null);
created = !modified;
asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString());
asset.Description = xml.GetAttribute("desc");
asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0;
asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0;
asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", ""));
Rest.AssetServices.Store(asset);
if (created)
{
rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
rdata.Complete(Rest.HttpStatusCodeCreated);
}
else
{
if (modified)
{
rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
rdata.Complete(Rest.HttpStatusCodeOK);
}
else
{
rdata.Complete(Rest.HttpStatusCodeNoContent);
}
}
rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
}
/// <summary>
/// Asset processing has no special data area requirements.
/// </summary>
internal class AssetRequestData : RequestData
{
internal AssetRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
: base(request, response, prefix)
{
}
}
}
}

View File

@ -0,0 +1,448 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Xml;
using System.IO;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class RestFileServices : IRest
{
private bool enabled = false;
private string qPrefix = "files";
// A simple constructor is used to handle any once-only
// initialization of working classes.
public RestFileServices()
{
Rest.Log.InfoFormat("{0} File services initializing", MsgId);
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
// If the handler specifies a relative path for its domain
// then we must add the standard absolute prefix, e.g. /admin
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
{
Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix);
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix);
}
// Register interface using the fully-qualified prefix
Rest.Plugin.AddPathHandler(DoFile, qPrefix, Allocate);
// Activate if all went OK
enabled = true;
Rest.Log.InfoFormat("{0} File services initialization complete", MsgId);
}
// Post-construction, pre-enabled initialization opportunity
// Not currently exploited.
public void Initialize()
{
}
// Called by the plug-in to halt REST processing. Local processing is
// disabled, and control blocks until all current processing has
// completed. No new processing will be started
public void Close()
{
enabled = false;
Rest.Log.InfoFormat("{0} File services ({1}) closing down", MsgId, qPrefix);
}
// Properties
internal string MsgId
{
get { return Rest.MsgId; }
}
#region Interface
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
{
return (RequestData) new FileRequestData(request, response, prefix);
}
// Asset Handler
private void DoFile(RequestData rparm)
{
if (!enabled) return;
FileRequestData rdata = (FileRequestData) rparm;
Rest.Log.DebugFormat("{0} REST File handler ({1}) ENTRY", MsgId, qPrefix);
// Now that we know this is a serious attempt to
// access file data, we should find out who
// is asking, and make sure they are authorized
// to do so. We need to validate the caller's
// identity before revealing anything about the
// status quo. Authenticate throws an exception
// via Fail if no identity information is present.
//
// With the present HTTP server we can't use the
// builtin authentication mechanisms because they
// would be enforced for all in-bound requests.
// Instead we look at the headers ourselves and
// handle authentication directly.
try
{
if (!rdata.IsAuthenticated)
{
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
}
}
catch (RestException e)
{
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
{
Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
rdata.request.Headers.Get("Authorization"));
}
else
{
Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
rdata.request.Headers.Get("Authorization"));
}
throw (e);
}
// Remove the prefix and what's left are the parameters. If we don't have
// the parameters we need, fail the request. Parameters do NOT include
// any supplied query values.
if (rdata.Parameters.Length > 0)
{
switch (rdata.method)
{
case "get" :
DoGet(rdata);
break;
case "put" :
DoPut(rdata);
break;
case "post" :
DoPost(rdata);
break;
case "delete" :
DoDelete(rdata);
break;
default :
Rest.Log.WarnFormat("{0} File: Method not supported: {1}",
MsgId, rdata.method);
rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
break;
}
}
else
{
Rest.Log.WarnFormat("{0} File: No agent information provided", MsgId);
rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
}
Rest.Log.DebugFormat("{0} REST File handler EXIT", MsgId);
}
#endregion Interface
/// <summary>
/// The only parameter we recognize is a UUID.If an asset with this identification is
/// found, it's content, base-64 encoded, is returned to the client.
/// </summary>
private void DoGet(FileRequestData rdata)
{
string path = String.Empty;
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
if (rdata.Parameters.Length > 1)
{
try
{
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
if (File.Exists(path))
{
Rest.Log.DebugFormat("{0} File located <{1}>", MsgId, path);
Byte[] data = File.ReadAllBytes(path);
rdata.initXmlWriter();
rdata.writer.WriteStartElement(String.Empty,"File",String.Empty);
rdata.writer.WriteAttributeString("name", path);
rdata.writer.WriteBase64(data,0,data.Length);
rdata.writer.WriteFullEndElement();
}
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, path);
rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0}", path));
}
}
catch (Exception e)
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, e.Message);
rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
path, e.Message));
}
}
rdata.Complete();
rdata.Respond(String.Format("File <{0}> : Normal completion", rdata.method));
}
/// <summary>
/// UPDATE existing item, if it exists. URI identifies the item in question.
/// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded)
/// is decoded and stored in the database, identified by the supplied UUID.
/// </summary>
private void DoPut(FileRequestData rdata)
{
bool modified = false;
bool created = false;
string path = String.Empty;
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
if (rdata.Parameters.Length > 1)
{
try
{
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
bool maymod = File.Exists(path);
rdata.initXmlReader();
XmlReader xml = rdata.reader;
if (!xml.ReadToFollowing("File"))
{
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
}
Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
File.WriteAllBytes(path,data);
modified = maymod;
created = ! maymod;
}
catch (Exception e)
{
Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
e.Message);
}
}
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
}
if (created)
{
rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
rdata.Complete(Rest.HttpStatusCodeCreated);
}
else
{
if (modified)
{
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
rdata.Complete(Rest.HttpStatusCodeOK);
}
else
{
rdata.Complete(Rest.HttpStatusCodeNoContent);
}
}
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
}
/// <summary>
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
/// No parameters are required for POST, just thepayload.
/// </summary>
private void DoPost(FileRequestData rdata)
{
bool modified = false;
bool created = false;
string path = String.Empty;
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
if (rdata.Parameters.Length > 1)
{
try
{
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
bool maymod = File.Exists(path);
rdata.initXmlReader();
XmlReader xml = rdata.reader;
if (!xml.ReadToFollowing("File"))
{
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
}
Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
File.WriteAllBytes(path,data);
modified = maymod;
created = ! maymod;
}
catch (Exception e)
{
Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
e.Message);
}
}
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
}
if (created)
{
rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
rdata.Complete(Rest.HttpStatusCodeCreated);
}
else
{
if (modified)
{
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
rdata.Complete(Rest.HttpStatusCodeOK);
}
else
{
rdata.Complete(Rest.HttpStatusCodeNoContent);
}
}
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
}
/// <summary>
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
/// No parameters are required for POST, just thepayload.
/// </summary>
private void DoDelete(FileRequestData rdata)
{
bool modified = false;
bool created = false;
string path = String.Empty;
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
if (rdata.Parameters.Length > 1)
{
try
{
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
if (File.Exists(path))
{
File.Delete(path);
}
}
catch (Exception e)
{
Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
e.Message);
rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
path, e.Message));
}
}
else
{
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
}
if (created)
{
rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
rdata.Complete(Rest.HttpStatusCodeCreated);
}
else
{
if (modified)
{
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
rdata.Complete(Rest.HttpStatusCodeOK);
}
else
{
rdata.Complete(Rest.HttpStatusCodeNoContent);
}
}
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
}
/// <summary>
/// File processing has no special data area requirements.
/// </summary>
internal class FileRequestData : RequestData
{
internal FileRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
: base(request, response, prefix)
{
}
}
}
}

View File

@ -0,0 +1,658 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
/// <remarks>
/// The class signature reveals the roles that RestHandler plays.
///
/// [1] It is a sub-class of RestPlugin. It inherits and extends
/// the functionality of this class, constraining it to the
/// specific needs of this REST implementation. This relates
/// to the plug-in mechanism supported by OpenSim, the specifics
/// of which are mostly hidden by RestPlugin.
/// [2] IRestHandler describes the interface that this class
/// exports to service implementations. This is the services
/// management interface.
/// [3] IHttpAgentHandler describes the interface that is exported
/// to the BaseHttpServer in support of this particular HTTP
/// processing model. This is the request interface of the
/// handler.
/// </remarks>
public class RestHandler : RestPlugin, IRestHandler, IHttpAgentHandler
{
// Handler tables: both stream and REST are supported. The path handlers and their
// respective allocators are stored in separate tables.
internal Dictionary<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>();
internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>();
internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>();
#region local static state
private static bool handlersLoaded = false;
private static List<Type> classes = new List<Type>();
private static List<IRest> handlers = new List<IRest>();
private static Type[] parms = new Type[0];
private static Object[] args = new Object[0];
/// <summary>
/// This static initializer scans the ASSEMBLY for classes that
/// export the IRest interface and builds a list of them. These
/// are later activated by the handler. To add a new handler it
/// is only necessary to create a new services class that implements
/// the IRest interface, and recompile the handler. This gives
/// all of the build-time flexibility of a modular approach
/// while not introducing yet-another module loader. Note that
/// multiple assembles can still be built, each with its own set
/// of handlers. Examples of services classes are RestInventoryServices
/// and RestSkeleton.
/// </summary>
static RestHandler()
{
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
foreach (Module m in mods)
{
Type[] types = m.GetTypes();
foreach (Type t in types)
{
try
{
if (t.GetInterface("IRest") != null)
{
classes.Add(t);
}
}
catch (Exception)
{
Rest.Log.WarnFormat("[STATIC-HANDLER]: #0 Error scanning {1}", t);
Rest.Log.InfoFormat("[STATIC-HANDLER]: #0 {1} is not included", t);
}
}
}
}
#endregion local static state
#region local instance state
/// <summary>
/// This routine loads all of the handlers discovered during
/// instance initialization.
/// A table of all loaded and successfully constructed handlers
/// is built, and this table is then used by the constructor to
/// initialize each of the handlers in turn.
/// NOTE: The loading process does not automatically imply that
/// the handler has registered any kind of an interface, that
/// may be (optionally) done by the handler either during
/// construction, or during initialization.
///
/// I was not able to make this code work within a constructor
/// so it is isolated within this method.
/// </summary>
private void LoadHandlers()
{
lock (handlers)
{
if (!handlersLoaded)
{
ConstructorInfo ci;
Object ht;
foreach (Type t in classes)
{
try
{
ci = t.GetConstructor(parms);
ht = ci.Invoke(args);
handlers.Add((IRest)ht);
}
catch (Exception e)
{
Rest.Log.WarnFormat("{0} Unable to load {1} : {2}", MsgId, t, e.Message);
}
}
handlersLoaded = true;
}
}
}
#endregion local instance state
#region overriding properties
// These properties override definitions
// in the base class.
// Name is used to differentiate the message header.
public override string Name
{
get { return "HANDLER"; }
}
// Used to partition the .ini configuration space.
public override string ConfigName
{
get { return "RestHandler"; }
}
// We have to rename these because we want
// to be able to share the values with other
// classes in our assembly and the base
// names are protected.
public string MsgId
{
get { return base.MsgID; }
}
public string RequestId
{
get { return base.RequestID; }
}
#endregion overriding properties
#region overriding methods
/// <summary>
/// This method is called by OpenSimMain immediately after loading the
/// plugin and after basic server setup, but before running any server commands.
/// </summary>
/// <remarks>
/// Note that entries MUST be added to the active configuration files before
/// the plugin can be enabled.
/// </remarks>
public override void Initialise(OpenSimBase openSim)
{
try
{
// This plugin will only be enabled if the broader
// REST plugin mechanism is enabled.
//Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId);
base.Initialise(openSim);
// IsEnabled is implemented by the base class and
// reflects an overall RestPlugin status
if (!IsEnabled)
{
//Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId);
return;
}
Rest.Log.InfoFormat("{0} Rest <{1}> plugin will be enabled", MsgId, Name);
Rest.Log.InfoFormat("{0} Configuration parameters read from <{1}>", MsgId, ConfigName);
// These are stored in static variables to make
// them easy to reach from anywhere in the assembly.
Rest.main = openSim;
if (Rest.main == null)
throw new Exception("OpenSim base pointer is null");
Rest.Plugin = this;
Rest.Config = Config;
Rest.Prefix = Prefix;
Rest.GodKey = GodKey;
Rest.Authenticate = Rest.Config.GetBoolean("authenticate", Rest.Authenticate);
Rest.Scheme = Rest.Config.GetString("auth-scheme", Rest.Scheme);
Rest.Secure = Rest.Config.GetBoolean("secured", Rest.Secure);
Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape", Rest.ExtendedEscape);
Rest.Realm = Rest.Config.GetString("realm", Rest.Realm);
Rest.DumpAsset = Rest.Config.GetBoolean("dump-asset", Rest.DumpAsset);
Rest.Fill = Rest.Config.GetBoolean("path-fill", Rest.Fill);
Rest.DumpLineSize = Rest.Config.GetInt("dump-line-size", Rest.DumpLineSize);
Rest.FlushEnabled = Rest.Config.GetBoolean("flush-on-error", Rest.FlushEnabled);
// Note: Odd spacing is required in the following strings
Rest.Log.InfoFormat("{0} Authentication is {1}required", MsgId,
(Rest.Authenticate ? "" : "not "));
Rest.Log.InfoFormat("{0} Security is {1}enabled", MsgId,
(Rest.Secure ? "" : "not "));
Rest.Log.InfoFormat("{0} Extended URI escape processing is {1}enabled", MsgId,
(Rest.ExtendedEscape ? "" : "not "));
Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
(Rest.DumpAsset ? "" : "not "));
// The supplied prefix MUST be absolute
if (Rest.Prefix.Substring(0,1) != Rest.UrlPathSeparator)
{
Rest.Log.WarnFormat("{0} Prefix <{1}> is not absolute and must be", MsgId, Rest.Prefix);
Rest.Log.InfoFormat("{0} Prefix changed to </{1}>", MsgId, Rest.Prefix);
Rest.Prefix = String.Format("{0}{1}", Rest.UrlPathSeparator, Rest.Prefix);
}
// If data dumping is requested, report on the chosen line
// length.
if (Rest.DumpAsset)
{
Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId, Rest.DumpLineSize);
}
// Load all of the handlers present in the
// assembly
// In principle, as we're an application plug-in,
// most of what needs to be done could be done using
// static resources, however the Open Sim plug-in
// model makes this an instance, so that's what we
// need to be.
// There is only one Communications manager per
// server, and by inference, only one each of the
// user, asset, and inventory servers. So we can cache
// those using a static initializer.
// We move all of this processing off to another
// services class to minimize overlap between function
// and infrastructure.
LoadHandlers();
// The intention of a post construction initializer
// is to allow for setup that is dependent upon other
// activities outside of the agency.
foreach (IRest handler in handlers)
{
try
{
handler.Initialize();
}
catch (Exception e)
{
Rest.Log.ErrorFormat("{0} initialization error: {1}", MsgId, e.Message);
}
}
// Now that everything is setup we can proceed to
// add THIS agent to the HTTP server's handler list
if (!AddAgentHandler(Rest.Name,this))
{
Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
foreach (IRest handler in handlers)
{
handler.Close();
}
}
}
catch (Exception e)
{
Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message);
}
}
/// <summary>
/// In the interests of efficiency, and because we cannot determine whether
/// or not this instance will actually be harvested, we clobber the only
/// anchoring reference to the working state for this plug-in. What the
/// call to close does is irrelevant to this class beyond knowing that it
/// can nullify the reference when it returns.
/// To make sure everything is copacetic we make sure the primary interface
/// is disabled by deleting the handler from the HTTP server tables.
/// </summary>
public override void Close()
{
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
try
{
RemoveAgentHandler(Rest.Name, this);
}
catch (KeyNotFoundException){}
foreach (IRest handler in handlers)
{
handler.Close();
}
}
#endregion overriding methods
#region interface methods
/// <summary>
/// This method is called by the HTTP server to match an incoming
/// request. It scans all of the strings registered by the
/// underlying handlers and looks for the best match. It returns
/// true if a match is found.
/// The matching process could be made arbitrarily complex.
/// Note: The match is case-insensitive.
/// </summary>
public bool Match(OSHttpRequest request, OSHttpResponse response)
{
string path = request.RawUrl.ToLower();
// Rest.Log.DebugFormat("{0} Match ENTRY", MsgId);
try
{
foreach (string key in pathHandlers.Keys)
{
// Rest.Log.DebugFormat("{0} Match testing {1} against agent prefix <{2}>", MsgId, path, key);
// Note that Match will not necessarily find the handler that will
// actually be used - it does no test for the "closest" fit. It
// simply reflects that at least one possible handler exists.
if (path.StartsWith(key))
{
// Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
// This apparently odd evaluation is needed to prevent a match
// on anything other than a URI token boundary. Otherwise we
// may match on URL's that were not intended for this handler.
return (path.Length == key.Length ||
path.Substring(key.Length, 1) == Rest.UrlPathSeparator);
}
}
path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path);
foreach (string key in streamHandlers.Keys)
{
// Rest.Log.DebugFormat("{0} Match testing {1} against stream prefix <{2}>", MsgId, path, key);
// Note that Match will not necessarily find the handler that will
// actually be used - it does no test for the "closest" fit. It
// simply reflects that at least one possible handler exists.
if (path.StartsWith(key))
{
// Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
// This apparently odd evaluation is needed to prevent a match
// on anything other than a URI token boundary. Otherwise we
// may match on URL's that were not intended for this handler.
return (path.Length == key.Length ||
path.Substring(key.Length, 1) == Rest.UrlPathSeparator);
}
}
}
catch (Exception e)
{
Rest.Log.ErrorFormat("{0} matching exception for path <{1}> : {2}", MsgId, path, e.Message);
}
return false;
}
/// <summary>
/// This is called by the HTTP server once the handler has indicated
/// that it is able to handle the request.
/// Preconditions:
/// [1] request != null and is a valid request object
/// [2] response != null and is a valid response object
/// Behavior is undefined if preconditions are not satisfied.
/// </summary>
public bool Handle(OSHttpRequest request, OSHttpResponse response)
{
bool handled;
base.MsgID = base.RequestID;
// Debug only
if (Rest.DEBUG)
{
Rest.Log.DebugFormat("{0} ENTRY", MsgId);
Rest.Log.DebugFormat("{0} Agent: {1}", MsgId, request.UserAgent);
Rest.Log.DebugFormat("{0} Method: {1}", MsgId, request.HttpMethod);
for (int i = 0; i < request.Headers.Count; i++)
{
Rest.Log.DebugFormat("{0} Header [{1}] : <{2}> = <{3}>",
MsgId, i, request.Headers.GetKey(i), request.Headers.Get(i));
}
Rest.Log.DebugFormat("{0} URI: {1}", MsgId, request.RawUrl);
}
// If a path handler worked we're done, otherwise try any
// available stream handlers too.
try
{
handled = (FindPathHandler(request, response) ||
FindStreamHandler(request, response));
}
catch (Exception e)
{
// A raw exception indicates that something we weren't expecting has
// happened. This should always reflect a shortcoming in the plugin,
// or a failure to satisfy the preconditions. It should not reflect
// an error in the request itself. Under such circumstances the state
// of the request cannot be determined and we are obliged to mark it
// as 'handled'.
Rest.Log.ErrorFormat("{0} Plugin error: {1}", MsgId, e.Message);
handled = true;
}
Rest.Log.DebugFormat("{0} EXIT", MsgId);
return handled;
}
#endregion interface methods
/// <summary>
/// If there is a stream handler registered that can handle the
/// request, then fine. If the request is not matched, do
/// nothing.
/// Note: The selection is case-insensitive
/// </summary>
private bool FindStreamHandler(OSHttpRequest request, OSHttpResponse response)
{
RequestData rdata = new RequestData(request, response, String.Empty);
string bestMatch = String.Empty;
string path = String.Format("{0}:{1}", rdata.method, rdata.path).ToLower();
Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path);
if (!IsEnabled)
{
return false;
}
foreach (string pattern in streamHandlers.Keys)
{
if (path.StartsWith(pattern))
{
if (pattern.Length > bestMatch.Length)
{
bestMatch = pattern;
}
}
}
// Handle using the best match available
if (bestMatch.Length > 0)
{
Rest.Log.DebugFormat("{0} Stream-based handler matched with <{1}>", MsgId, bestMatch);
RestStreamHandler handler = streamHandlers[bestMatch];
rdata.buffer = handler.Handle(rdata.path, rdata.request.InputStream, rdata.request, rdata.response);
rdata.AddHeader(rdata.response.ContentType,handler.ContentType);
rdata.Respond("FindStreamHandler Completion");
}
return rdata.handled;
}
/// <summary>
/// Add a stream handler for the designated HTTP method and path prefix.
/// If the handler is not enabled, the request is ignored. If the path
/// does not start with the REST prefix, it is added. If method-qualified
/// path has not already been registered, the method is added to the active
/// handler table.
/// </summary>
public void AddStreamHandler(string httpMethod, string path, RestMethod method)
{
if (!IsEnabled)
{
return;
}
if (!path.StartsWith(Rest.Prefix))
{
path = String.Format("{0}{1}", Rest.Prefix, path);
}
path = String.Format("{0}{1}{2}", httpMethod, Rest.UrlMethodSeparator, path);
// Conditionally add to the list
if (!streamHandlers.ContainsKey(path))
{
streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method));
Rest.Log.DebugFormat("{0} Added handler for {1}", MsgId, path);
}
else
{
Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path);
}
}
/// <summary>
/// Given the supplied request/response, if the handler is enabled, the inbound
/// information is used to match an entry in the active path handler tables, using
/// the method-qualified path information. If a match is found, then the handler is
/// invoked. The result is the boolean result of the handler, or false if no
/// handler was located. The boolean indicates whether or not the request has been
/// handled, not whether or not the request was successful - that information is in
/// the response.
/// Note: The selection process is case-insensitive
/// </summary>
internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
{
RequestData rdata = null;
string bestMatch = null;
if (!IsEnabled)
{
return false;
}
// Conditionally add to the list
Rest.Log.DebugFormat("{0} Checking for path handler for <{1}>", MsgId, request.RawUrl);
foreach (string pattern in pathHandlers.Keys)
{
if (request.RawUrl.ToLower().StartsWith(pattern))
{
if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
{
bestMatch = pattern;
}
}
}
if (!String.IsNullOrEmpty(bestMatch))
{
rdata = pathAllocators[bestMatch](request, response, bestMatch);
Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch);
try
{
pathHandlers[bestMatch](rdata);
}
// A plugin generated error indicates a request-related error
// that has been handled by the plugin.
catch (RestException r)
{
Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message);
}
}
return (rdata == null) ? false : rdata.handled;
}
/// <summary>
/// A method handler and a request allocator are stored using the designated
/// path as a key. If an entry already exists, it is replaced by the new one.
/// </summary>
public void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
{
if (!IsEnabled)
{
return;
}
if (pathHandlers.ContainsKey(path))
{
Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path);
pathHandlers.Remove(path);
}
if (pathAllocators.ContainsKey(path))
{
Rest.Log.DebugFormat("{0} Replacing allocator for <${1}>", MsgId, path);
pathAllocators.Remove(path);
}
Rest.Log.DebugFormat("{0} Adding path handler for {1}", MsgId, path);
pathHandlers.Add(path, mh);
pathAllocators.Add(path, ra);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
public class RestTestServices : IRest
{
private bool enabled = false;
private string qPrefix = "test";
// A simple constructor is used to handle any once-only
// initialization of working classes.
public RestTestServices()
{
Rest.Log.InfoFormat("{0} Test services initializing", MsgId);
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
// If a relative path was specified, make it absolute by adding
// the standard prefix, e.g. /admin
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
{
Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
}
// Load test cases
loadTests();
foreach (ITest test in tests)
{
test.Initialize();
}
// Register interface
Rest.Plugin.AddPathHandler(DoTests,qPrefix,Allocate);
// Activate
enabled = true;
Rest.Log.InfoFormat("{0} Test services initialization complete", MsgId);
}
// Post-construction, pre-enabled initialization opportunity
// Not currently exploited.
public void Initialize()
{
}
// Called by the plug-in to halt REST processing. Local processing is
// disabled, and control blocks until all current processing has
// completed. No new processing will be started
public void Close()
{
enabled = false;
foreach (ITest test in tests)
{
test.Close();
}
Rest.Log.InfoFormat("{0} Test services closing down", MsgId);
}
// Properties
internal string MsgId
{
get { return Rest.MsgId; }
}
#region Interface
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
{
return new RequestData(request, response, prefix);
}
// Inventory Handler
private void DoTests(RequestData rdata)
{
if (!enabled)
return;
// Now that we know this is a serious attempt to
// access inventory data, we should find out who
// is asking, and make sure they are authorized
// to do so. We need to validate the caller's
// identity before revealing anything about the
// status quo. Authenticate throws an exception
// via Fail if no identity information is present.
//
// With the present HTTP server we can't use the
// builtin authentication mechanisms because they
// would be enforced for all in-bound requests.
// Instead we look at the headers ourselves and
// handle authentication directly.
try
{
if (!rdata.IsAuthenticated)
{
rdata.Fail(Rest.HttpStatusCodeNotAuthorized,
String.Format("user \"{0}\" could not be authenticated", rdata.userName));
}
}
catch (RestException e)
{
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
{
Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
}
else
{
Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
}
throw (e);
}
// Check that a test was specified
if (rdata.Parameters.Length < 1)
{
Rest.Log.DebugFormat("{0} Insufficient parameters", MsgId);
rdata.Fail(Rest.HttpStatusCodeBadRequest, "not enough parameters");
}
// Select the test
foreach (ITest test in tests)
{
if (!rdata.handled)
test.Execute(rdata);
}
}
#endregion Interface
private static bool testsLoaded = false;
private static List<Type> classes = new List<Type>();
private static List<ITest> tests = new List<ITest>();
private static Type[] parms = new Type[0];
private static Object[] args = new Object[0];
static RestTestServices()
{
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
foreach (Module m in mods)
{
Type[] types = m.GetTypes();
foreach (Type t in types)
{
try
{
if (t.GetInterface("ITest") != null)
{
classes.Add(t);
}
}
catch (Exception e)
{
Rest.Log.WarnFormat("[STATIC-TEST] Unable to include test {0} : {1}", t, e.Message);
}
}
}
}
/// <summary>
/// This routine loads all of the handlers discovered during
/// instance initialization. Each handler is responsible for
/// registering itself with this handler.
/// I was not able to make this code work in a constructor.
/// </summary>
private void loadTests()
{
lock (tests)
{
if (!testsLoaded)
{
ConstructorInfo ci;
Object ht;
foreach (Type t in classes)
{
try
{
if (t.GetInterface("ITest") != null)
{
ci = t.GetConstructor(parms);
ht = ci.Invoke(args);
tests.Add((ITest)ht);
Rest.Log.InfoFormat("{0} Test {1} added", MsgId, t);
}
}
catch (Exception e)
{
Rest.Log.WarnFormat("{0} Unable to load test {1} : {2}", MsgId, t, e.Message);
}
}
testsLoaded = true;
}
}
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OpenSim.ApplicationPlugins.Rest.Inventory
{
/// <summary>
/// This interface represents the boundary between the general purpose
/// REST plugin handling, and the functionally specific handlers. The
/// handler knows only to initialzie and terminate all such handlers
/// that it finds.
/// </summary>
internal interface ITest
{
void Initialize();
void Execute(RequestData rdata);
void Close();
}
}

View File

@ -0,0 +1,204 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using OpenMetaverse;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.ApplicationPlugins.Rest.Inventory.Tests
{
public class Remote : ITest
{
private static readonly int PARM_TESTID = 0;
private static readonly int PARM_COMMAND = 1;
private static readonly int PARM_MOVE_AVATAR = 2;
private static readonly int PARM_MOVE_X = 3;
private static readonly int PARM_MOVE_Y = 4;
private static readonly int PARM_MOVE_Z = 5;
private bool enabled = false;
// No constructor code is required.
public Remote()
{
Rest.Log.InfoFormat("{0} Remote services constructor", MsgId);
}
// Post-construction, pre-enabled initialization opportunity
// Not currently exploited.
public void Initialize()
{
enabled = true;
Rest.Log.InfoFormat("{0} Remote services initialized", MsgId);
}
// Called by the plug-in to halt REST processing. Local processing is
// disabled, and control blocks until all current processing has
// completed. No new processing will be started
public void Close()
{
enabled = false;
Rest.Log.InfoFormat("{0} Remote services closing down", MsgId);
}
// Properties
internal string MsgId
{
get { return Rest.MsgId; }
}
// Remote Handler
// Key information of interest here is the Parameters array, each
// entry represents an element of the URI, with element zero being
// the
public void Execute(RequestData rdata)
{
if (!enabled) return;
// If we can't relate to what's there, leave it for others.
if (rdata.Parameters.Length == 0 || rdata.Parameters[PARM_TESTID] != "remote")
return;
Rest.Log.DebugFormat("{0} REST Remote handler ENTRY", MsgId);
// Remove the prefix and what's left are the parameters. If we don't have
// the parameters we need, fail the request. Parameters do NOT include
// any supplied query values.
if (rdata.Parameters.Length > 1)
{
switch (rdata.Parameters[PARM_COMMAND].ToLower())
{
case "move" :
DoMove(rdata);
break;
default :
DoHelp(rdata);
break;
}
}
else
{
DoHelp(rdata);
}
}
private void DoHelp(RequestData rdata)
{
rdata.body = Help;
rdata.Complete();
rdata.Respond("Help");
}
private void DoMove(RequestData rdata)
{
if (rdata.Parameters.Length < 6)
{
Rest.Log.WarnFormat("{0} Move: No movement information provided", MsgId);
rdata.Fail(Rest.HttpStatusCodeBadRequest, "no movement information provided");
}
else
{
string[] names = rdata.Parameters[PARM_MOVE_AVATAR].Split(Rest.CA_SPACE);
ScenePresence presence = null;
Scene scene = null;
if (names.Length != 2)
{
rdata.Fail(Rest.HttpStatusCodeBadRequest,
String.Format("invalid avatar name: <{0}>",rdata.Parameters[PARM_MOVE_AVATAR]));
}
Rest.Log.WarnFormat("{0} '{1}' command received for {2} {3}",
MsgId, rdata.Parameters[0], names[0], names[1]);
// The first parameter should be an avatar name, look for the
// avatar in the known regions first.
Rest.main.SceneManager.ForEachScene(delegate(Scene s)
{
s.ForEachRootScenePresence(delegate(ScenePresence sp)
{
if (sp.Firstname == names[0] && sp.Lastname == names[1])
{
scene = s;
presence = sp;
}
});
});
if (presence != null)
{
Rest.Log.DebugFormat("{0} Move : Avatar {1} located in region {2}",
MsgId, rdata.Parameters[PARM_MOVE_AVATAR], scene.RegionInfo.RegionName);
try
{
float x = Convert.ToSingle(rdata.Parameters[PARM_MOVE_X]);
float y = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Y]);
float z = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Z]);
Vector3 vector = new Vector3(x, y, z);
presence.MoveToTarget(vector, false, false);
}
catch (Exception e)
{
rdata.Fail(Rest.HttpStatusCodeBadRequest,
String.Format("invalid parameters: {0}", e.Message));
}
}
else
{
rdata.Fail(Rest.HttpStatusCodeBadRequest,
String.Format("avatar {0} not present", rdata.Parameters[PARM_MOVE_AVATAR]));
}
rdata.Complete();
rdata.Respond("OK");
}
}
private static readonly string Help =
"<html>"
+ "<head><title>Remote Command Usage</title></head>"
+ "<body>"
+ "<p>Supported commands are:</p>"
+ "<dl>"
+ "<dt>move/avatar-name/x/y/z</dt>"
+ "<dd>moves the specified avatar to another location</dd>"
+ "</dl>"
+ "</body>"
+ "</html>"
;
}
}

View File

@ -0,0 +1,228 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using System.Xml.Serialization;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.ApplicationPlugins.Rest.Regions
{
public partial class RestRegionPlugin : RestPlugin
{
#region GET methods
public string GetHandler(string request, string path, string param,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
// foreach (string h in httpRequest.Headers.AllKeys)
// foreach (string v in httpRequest.Headers.GetValues(h))
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
MsgID = RequestID;
m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param);
try
{
// param empty: regions list
if (String.IsNullOrEmpty(param)) return GetHandlerRegions(httpResponse);
// param not empty: specific region
return GetHandlerRegion(httpResponse, param);
}
catch (Exception e)
{
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e);
}
}
public string GetHandlerRegions(IOSHttpResponse httpResponse)
{
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
foreach (Scene s in App.SceneManager.Scenes)
{
rxw.WriteStartElement(String.Empty, "uuid", String.Empty);
rxw.WriteString(s.RegionInfo.RegionID.ToString());
rxw.WriteEndElement();
}
rxw.WriteEndElement();
return rxw.ToString();
}
protected string ShortRegionInfo(string key, string value)
{
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
if (String.IsNullOrEmpty(value) ||
String.IsNullOrEmpty(key)) return null;
rxw.WriteStartElement(String.Empty, "region", String.Empty);
rxw.WriteStartElement(String.Empty, key, String.Empty);
rxw.WriteString(value);
rxw.WriteEndDocument();
return rxw.ToString();
}
public string GetHandlerRegion(IOSHttpResponse httpResponse, string param)
{
// be resilient and don't get confused by a terminating '/'
param = param.TrimEnd(new char[]{'/'});
string[] comps = param.Split('/');
UUID regionID = (UUID)comps[0];
m_log.DebugFormat("{0} GET region UUID {1}", MsgID, regionID.ToString());
if (UUID.Zero == regionID) throw new Exception("missing region ID");
Scene scene = null;
App.SceneManager.TryGetScene(regionID, out scene);
if (null == scene) return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
"GET", "cannot find region {0}", regionID.ToString());
RegionDetails details = new RegionDetails(scene.RegionInfo);
// m_log.DebugFormat("{0} GET comps {1}", MsgID, comps.Length);
// for (int i = 0; i < comps.Length; i++) m_log.DebugFormat("{0} GET comps[{1}] >{2}<", MsgID, i, comps[i]);
if (1 == comps.Length)
{
// complete region details requested
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
XmlSerializer xs = new XmlSerializer(typeof(RegionDetails));
xs.Serialize(rxw, details, _xmlNs);
return rxw.ToString();
}
if (2 == comps.Length)
{
string resp = ShortRegionInfo(comps[1], details[comps[1]]);
if (null != resp) return resp;
// m_log.DebugFormat("{0} GET comps advanced: >{1}<", MsgID, comps[1]);
// check for {terrain,stats,prims}
switch (comps[1].ToLower())
{
case "terrain":
return RegionTerrain(httpResponse, scene);
case "stats":
return RegionStats(httpResponse, scene);
case "prims":
return RegionPrims(httpResponse, scene, Vector3.Zero, Vector3.Zero);
}
}
if (3 == comps.Length)
{
switch (comps[1].ToLower())
{
case "prims":
string[] subregion = comps[2].Split(',');
if (subregion.Length == 6)
{
Vector3 min, max;
try
{
min = new Vector3((float)Double.Parse(subregion[0], Culture.NumberFormatInfo), (float)Double.Parse(subregion[1], Culture.NumberFormatInfo), (float)Double.Parse(subregion[2], Culture.NumberFormatInfo));
max = new Vector3((float)Double.Parse(subregion[3], Culture.NumberFormatInfo), (float)Double.Parse(subregion[4], Culture.NumberFormatInfo), (float)Double.Parse(subregion[5], Culture.NumberFormatInfo));
}
catch (Exception)
{
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
"GET", "invalid subregion parameter");
}
return RegionPrims(httpResponse, scene, min, max);
}
else
{
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
"GET", "invalid subregion parameter");
}
}
}
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
"GET", "too many parameters {0}", param);
}
#endregion GET methods
protected string RegionTerrain(IOSHttpResponse httpResponse, Scene scene)
{
httpResponse.SendChunked = true;
httpResponse.ContentType = "text/xml";
return scene.Heightmap.SaveToXmlString();
//return Failure(httpResponse, OSHttpStatusCode.ServerErrorNotImplemented,
// "GET", "terrain not implemented");
}
protected string RegionStats(IOSHttpResponse httpResponse, Scene scene)
{
int users = scene.GetRootAgentCount();
int objects = scene.Entities.Count - users;
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
rxw.WriteStartElement(String.Empty, "region", String.Empty);
rxw.WriteStartElement(String.Empty, "stats", String.Empty);
rxw.WriteStartElement(String.Empty, "users", String.Empty);
rxw.WriteString(users.ToString());
rxw.WriteEndElement();
rxw.WriteStartElement(String.Empty, "objects", String.Empty);
rxw.WriteString(objects.ToString());
rxw.WriteEndElement();
rxw.WriteEndDocument();
return rxw.ToString();
}
protected string RegionPrims(IOSHttpResponse httpResponse, Scene scene, Vector3 min, Vector3 max)
{
httpResponse.SendChunked = true;
httpResponse.ContentType = "text/xml";
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
if (serialiser != null)
serialiser.SavePrimsToXml2(scene, new StreamWriter(httpResponse.OutputStream), min, max);
return "";
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using System.Xml.Serialization;
using OpenMetaverse;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.ApplicationPlugins.Rest.Regions
{
public partial class RestRegionPlugin : RestPlugin
{
#region GET methods
public string GetRegionInfoHandler(string request, string path, string param,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
// foreach (string h in httpRequest.Headers.AllKeys)
// foreach (string v in httpRequest.Headers.GetValues(h))
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
MsgID = RequestID;
m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param);
try
{
// param empty: regions list
// if (String.IsNullOrEmpty(param))
return GetRegionInfoHandlerRegions(httpResponse);
// // param not empty: specific region
// return GetRegionInfoHandlerRegion(httpResponse, param);
}
catch (Exception e)
{
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e);
}
}
public string GetRegionInfoHandlerRegions(IOSHttpResponse httpResponse)
{
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
// regions info
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
{
// regions info: number of regions
rxw.WriteStartAttribute(String.Empty, "number", String.Empty);
rxw.WriteValue(App.SceneManager.Scenes.Count);
rxw.WriteEndAttribute();
// regions info: max number of regions
rxw.WriteStartAttribute(String.Empty, "max", String.Empty);
if (App.ConfigSource.Source.Configs["RemoteAdmin"] != null)
{
rxw.WriteValue(App.ConfigSource.Source.Configs["RemoteAdmin"].GetInt("region_limit", -1));
}
else
{
rxw.WriteValue(-1);
}
rxw.WriteEndAttribute();
// regions info: region
foreach (Scene s in App.SceneManager.Scenes)
{
rxw.WriteStartElement(String.Empty, "region", String.Empty);
rxw.WriteStartAttribute(String.Empty, "uuid", String.Empty);
rxw.WriteString(s.RegionInfo.RegionID.ToString());
rxw.WriteEndAttribute();
rxw.WriteStartAttribute(String.Empty, "name", String.Empty);
rxw.WriteString(s.RegionInfo.RegionName);
rxw.WriteEndAttribute();
rxw.WriteStartAttribute(String.Empty, "x", String.Empty);
rxw.WriteValue(s.RegionInfo.RegionLocX);
rxw.WriteEndAttribute();
rxw.WriteStartAttribute(String.Empty, "y", String.Empty);
rxw.WriteValue(s.RegionInfo.RegionLocY);
rxw.WriteEndAttribute();
rxw.WriteStartAttribute(String.Empty, "external_hostname", String.Empty);
rxw.WriteString(s.RegionInfo.ExternalHostName);
rxw.WriteEndAttribute();
rxw.WriteStartAttribute(String.Empty, "ip", String.Empty);
rxw.WriteString(s.RegionInfo.InternalEndPoint.ToString());
rxw.WriteEndAttribute();
int users = s.GetRootAgentCount();
rxw.WriteStartAttribute(String.Empty, "avatars", String.Empty);
rxw.WriteValue(users);
rxw.WriteEndAttribute();
rxw.WriteStartAttribute(String.Empty, "objects", String.Empty);
rxw.WriteValue(s.Entities.Count - users);
rxw.WriteEndAttribute();
rxw.WriteEndElement();
}
}
return rxw.ToString();
}
#endregion GET methods
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using OpenMetaverse;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace OpenSim.ApplicationPlugins.Rest.Regions
{
public partial class RestRegionPlugin : RestPlugin
{
#region POST methods
public string PostHandler(string request, string path, string param,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
// foreach (string h in httpRequest.Headers.AllKeys)
// foreach (string v in httpRequest.Headers.GetValues(h))
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
MsgID = RequestID;
m_log.DebugFormat("{0} POST path {1} param {2}", MsgID, path, param);
try
{
// param empty: new region post
if (!IsGod(httpRequest))
// XXX: this needs to be turned into a FailureUnauthorized(...)
return Failure(httpResponse, OSHttpStatusCode.ClientErrorUnauthorized,
"GET", "you are not god");
if (String.IsNullOrEmpty(param)) return CreateRegion(httpRequest, httpResponse);
// Parse region ID and other parameters
param = param.TrimEnd(new char[] {'/'});
string[] comps = param.Split('/');
UUID regionID = (UUID) comps[0];
m_log.DebugFormat("{0} POST region UUID {1}", MsgID, regionID.ToString());
if (UUID.Zero == regionID) throw new Exception("missing region ID");
Scene scene = null;
App.SceneManager.TryGetScene(regionID, out scene);
if (null == scene)
return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
"POST", "cannot find region {0}", regionID.ToString());
if (2 == comps.Length)
{
// check for {prims}
switch (comps[1].ToLower())
{
case "prims":
return LoadPrims(request, httpRequest, httpResponse, scene);
}
}
return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
"POST", "url {0} not supported", param);
}
catch (Exception e)
{
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "POST", e);
}
}
public string CreateRegion(IOSHttpRequest request, IOSHttpResponse response)
{
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
foreach (Scene s in App.SceneManager.Scenes)
{
rxw.WriteStartElement(String.Empty, "uuid", String.Empty);
rxw.WriteString(s.RegionInfo.RegionID.ToString());
rxw.WriteEndElement();
}
rxw.WriteEndElement();
return rxw.ToString();
}
public string LoadPrims(string requestBody, IOSHttpRequest request, IOSHttpResponse response, Scene scene)
{
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
if (serialiser != null)
serialiser.LoadPrimsFromXml2(scene, new StringReader(requestBody), true);
return "";
}
#endregion POST methods
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Xml.Serialization;
using OpenMetaverse;
using OpenSim.Framework;
namespace OpenSim.ApplicationPlugins.Rest.Regions
{
[XmlRoot(ElementName="region", IsNullable = false)]
public class RegionDetails
{
public string region_name;
public string region_id;
public uint region_x;
public uint region_y;
public string region_owner;
public string region_owner_id;
public uint region_http_port;
public uint region_port;
public string region_server_uri;
public string region_external_hostname;
public RegionDetails()
{
}
public RegionDetails(RegionInfo regInfo)
{
region_name = regInfo.RegionName;
region_id = regInfo.RegionID.ToString();
region_x = regInfo.RegionLocX;
region_y = regInfo.RegionLocY;
region_owner_id = regInfo.EstateSettings.EstateOwner.ToString();
region_http_port = regInfo.HttpPort;
region_server_uri = regInfo.ServerURI;
region_external_hostname = regInfo.ExternalHostName;
Uri uri = new Uri(region_server_uri);
region_port = (uint)uri.Port;
}
public string this[string idx]
{
get
{
switch (idx.ToLower())
{
case "name":
return region_name;
case "id":
return region_id;
case "location":
return String.Format("<x>{0}</x><y>{1}</y>", region_x, region_y);
case "owner":
return region_owner;
case "owner_id":
return region_owner_id;
case "http_port":
return region_http_port.ToString();
case "server_uri":
return region_server_uri;
case "external_hostname":
case "hostname":
return region_external_hostname;
default:
return null;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
<Addin id="OpenSim.ApplicationPlugins.Rest.Regions" version="0.1">
<Runtime>
<Import assembly="OpenSim.ApplicationPlugins.Rest.Regions.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/Startup">
<Plugin id="RestRegions" type="OpenSim.ApplicationPlugins.Rest.Regions.RestRegionPlugin" />
</Extension>
</Addin>

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Xml.Serialization;
namespace OpenSim.ApplicationPlugins.Rest.Regions
{
public partial class RestRegionPlugin : RestPlugin
{
private static XmlSerializerNamespaces _xmlNs;
static RestRegionPlugin()
{
_xmlNs = new XmlSerializerNamespaces();
_xmlNs.Add(String.Empty, String.Empty);
}
#region overriding properties
public override string Name
{
get { return "REGION"; }
}
public override string ConfigName
{
get { return "RestRegionPlugin"; }
}
#endregion overriding properties
#region overriding methods
/// <summary>
/// This method is called by OpenSimMain immediately after loading the
/// plugin and after basic server setup, but before running any server commands.
/// </summary>
/// <remarks>
/// Note that entries MUST be added to the active configuration files before
/// the plugin can be enabled.
/// </remarks>
public override void Initialise(OpenSimBase openSim)
{
try
{
base.Initialise(openSim);
if (!IsEnabled)
{
//m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID);
return;
}
m_log.InfoFormat("{0} REST region plugin enabled", MsgID);
// add REST method handlers
AddRestStreamHandler("GET", "/regions/", GetHandler);
AddRestStreamHandler("POST", "/regions/", PostHandler);
AddRestStreamHandler("GET", "/regioninfo/", GetRegionInfoHandler);
}
catch (Exception e)
{
m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message);
m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString());
}
}
public override void Close()
{
}
#endregion overriding methods
}
}

View File

@ -0,0 +1,415 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
namespace OpenSim.ApplicationPlugins.Rest
{
public abstract class RestPlugin : IApplicationPlugin
{
#region properties
protected static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IConfig _config; // Configuration source: Rest Plugins
private IConfig _pluginConfig; // Configuration source: Plugin specific
private OpenSimBase _app; // The 'server'
private BaseHttpServer _httpd; // The server's RPC interface
private string _prefix; // URL prefix below
// which all REST URLs
// are living
// private StringWriter _sw = null;
// private RestXmlWriter _xw = null;
private string _godkey;
private int _reqk;
[ThreadStatic]
private static string _threadRequestID = String.Empty;
/// <summary>
/// Return an ever increasing request ID for logging
/// </summary>
protected string RequestID
{
get { return _reqk++.ToString(); }
set { _reqk = Convert.ToInt32(value); }
}
/// <summary>
/// Thread-constant message IDs for logging.
/// </summary>
protected string MsgID
{
get { return String.Format("[REST-{0}] #{1}", Name, _threadRequestID); }
set { _threadRequestID = value; }
}
/// <summary>
/// Returns true if Rest Plugins are enabled.
/// </summary>
public bool PluginsAreEnabled
{
get { return null != _config; }
}
/// <summary>
/// Returns true if specific Rest Plugin is enabled.
/// </summary>
public bool IsEnabled
{
get
{
return (null != _pluginConfig) && _pluginConfig.GetBoolean("enabled", false);
}
}
/// <summary>
/// OpenSimMain application
/// </summary>
public OpenSimBase App
{
get { return _app; }
}
/// <summary>
/// RPC server
/// </summary>
public BaseHttpServer HttpServer
{
get { return _httpd; }
}
/// <summary>
/// URL prefix to use for all REST handlers
/// </summary>
public string Prefix
{
get { return _prefix; }
}
/// <summary>
/// Access to GOD password string
/// </summary>
protected string GodKey
{
get { return _godkey; }
}
/// <summary>
/// Configuration of the plugin
/// </summary>
public IConfig Config
{
get { return _pluginConfig; }
}
/// <summary>
/// Name of the plugin
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Return the config section name
/// </summary>
public abstract string ConfigName { get; }
// public XmlTextWriter XmlWriter
// {
// get
// {
// if (null == _xw)
// {
// _sw = new StringWriter();
// _xw = new RestXmlWriter(_sw);
// _xw.Formatting = Formatting.Indented;
// }
// return _xw;
// }
// }
// public string XmlWriterResult
// {
// get
// {
// _xw.Flush();
// _xw.Close();
// _xw = null;
// return _sw.ToString();
// }
// }
#endregion properties
#region methods
// TODO: required by IPlugin, but likely not at all right
private string m_version = "0.0";
public string Version
{
get { return m_version; }
}
public void Initialise()
{
m_log.Info("[RESTPLUGIN]: " + Name + " cannot be default-initialized!");
throw new PluginNotInitialisedException(Name);
}
/// <summary>
/// This method is called by OpenSimMain immediately after loading the
/// plugin and after basic server setup, but before running any server commands.
/// </summary>
/// <remarks>
/// Note that entries MUST be added to the active configuration files before
/// the plugin can be enabled.
/// </remarks>
public virtual void Initialise(OpenSimBase openSim)
{
RequestID = "0";
MsgID = RequestID;
try
{
if ((_config = openSim.ConfigSource.Source.Configs["RestPlugins"]) == null)
{
m_log.WarnFormat("{0} Rest Plugins not configured", MsgID);
return;
}
if (!_config.GetBoolean("enabled", false))
{
//m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID);
return;
}
_app = openSim;
_httpd = openSim.HttpServer;
// Retrieve GOD key value, if any.
_godkey = _config.GetString("god_key", String.Empty);
// Retrive prefix if any.
_prefix = _config.GetString("prefix", "/admin");
// Get plugin specific config
_pluginConfig = openSim.ConfigSource.Source.Configs[ConfigName];
m_log.InfoFormat("{0} Rest Plugins Enabled", MsgID);
}
catch (Exception e)
{
// we can safely ignore this, as it just means that
// the key lookup in Configs failed, which signals to
// us that noone is interested in our services...they
// don't know what they are missing out on...
// NOTE: Under the present OpenSimulator implementation it is
// not possible for the openSimulator pointer to be null. However
// were the implementation to be changed, this could
// result in a silent initialization failure. Harmless
// except for lack of function and lack of any
// diagnostic indication as to why. The same is true if
// the HTTP server reference is bad.
// We should at least issue a message...
m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message);
m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString());
}
}
public virtual void PostInitialise()
{
}
private List<RestStreamHandler> _handlers = new List<RestStreamHandler>();
private Dictionary<string, IHttpAgentHandler> _agents = new Dictionary<string, IHttpAgentHandler>();
/// <summary>
/// Add a REST stream handler to the underlying HTTP server.
/// </summary>
/// <param name="httpMethod">GET/PUT/POST/DELETE or
/// similar</param>
/// <param name="path">URL prefix</param>
/// <param name="method">RestMethod handler doing the actual work</param>
public virtual void AddRestStreamHandler(string httpMethod, string path, RestMethod method)
{
if (!IsEnabled) return;
if (!path.StartsWith(_prefix))
{
path = String.Format("{0}{1}", _prefix, path);
}
RestStreamHandler h = new RestStreamHandler(httpMethod, path, method);
_httpd.AddStreamHandler(h);
_handlers.Add(h);
m_log.DebugFormat("{0} Added REST handler {1} {2}", MsgID, httpMethod, path);
}
/// <summary>
/// Add a powerful Agent handler to the underlying HTTP
/// server.
/// </summary>
/// <param name="agentName">name of agent handler</param>
/// <param name="handler">agent handler method</param>
/// <returns>false when the plugin is disabled or the agent
/// handler could not be added. Any generated exceptions are
/// allowed to drop through to the caller, i.e. ArgumentException.
/// </returns>
public bool AddAgentHandler(string agentName, IHttpAgentHandler handler)
{
if (!IsEnabled) return false;
_agents.Add(agentName, handler);
return _httpd.AddAgentHandler(agentName, handler);
}
/// <summary>
/// Remove a powerful Agent handler from the underlying HTTP
/// server.
/// </summary>
/// <param name="agentName">name of agent handler</param>
/// <param name="handler">agent handler method</param>
/// <returns>false when the plugin is disabled or the agent
/// handler could not be removed. Any generated exceptions are
/// allowed to drop through to the caller, i.e. KeyNotFound.
/// </returns>
public bool RemoveAgentHandler(string agentName, IHttpAgentHandler handler)
{
if (!IsEnabled) return false;
if (_agents[agentName] == handler)
{
_agents.Remove(agentName);
return _httpd.RemoveAgentHandler(agentName, handler);
}
return false;
}
/// <summary>
/// Check whether the HTTP request came from god; that is, is
/// the god_key as configured in the config section supplied
/// via X-OpenSim-Godkey?
/// </summary>
/// <param name="request">HTTP request header</param>
/// <returns>true when the HTTP request came from god.</returns>
protected bool IsGod(IOSHttpRequest request)
{
string[] keys = request.Headers.GetValues("X-OpenSim-Godkey");
if (null == keys) return false;
// we take the last key supplied
return keys[keys.Length - 1] == _godkey;
}
/// <summary>
/// Checks wether the X-OpenSim-Password value provided in the
/// HTTP header is indeed the password on file for the avatar
/// specified by the UUID
/// </summary>
protected bool IsVerifiedUser(IOSHttpRequest request, UUID uuid)
{
// XXX under construction
return false;
}
/// <summary>
/// Clean up and remove all handlers that were added earlier.
/// </summary>
public virtual void Close()
{
foreach (RestStreamHandler h in _handlers)
{
_httpd.RemoveStreamHandler(h.HttpMethod, h.Path);
}
_handlers = null;
foreach (KeyValuePair<string, IHttpAgentHandler> h in _agents)
{
_httpd.RemoveAgentHandler(h.Key, h.Value);
}
_agents = null;
}
public virtual void Dispose()
{
Close();
}
/// <summary>
/// Return a failure message.
/// </summary>
/// <param name="method">origin of the failure message</param>
/// <param name="message">failure message</param>
/// <remarks>This should probably set a return code as
/// well. (?)</remarks>
protected string Failure(IOSHttpResponse response, OSHttpStatusCode status,
string method, string format, params string[] msg)
{
string m = String.Format(format, msg);
response.StatusCode = (int) status;
response.StatusDescription = m;
m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, m);
return String.Format("<error>{0}</error>", m);
}
/// <summary>
/// Return a failure message.
/// </summary>
/// <param name="method">origin of the failure message</param>
/// <param name="e">exception causing the failure message</param>
/// <remarks>This should probably set a return code as
/// well. (?)</remarks>
public string Failure(IOSHttpResponse response, OSHttpStatusCode status,
string method, Exception e)
{
string m = String.Format("exception occurred: {0}", e.Message);
response.StatusCode = (int) status;
response.StatusDescription = m;
m_log.DebugFormat("{0} {1} failed: {2}", MsgID, method, e.ToString());
m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, e.Message);
return String.Format("<error>{0}</error>", e.Message);
}
#endregion methods
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.IO;
using System.Text;
using System.Xml;
namespace OpenSim.ApplicationPlugins.Rest
{
public class RestXmlWriter: XmlTextWriter
{
private StringWriter m_sw = null;
public RestXmlWriter(StringWriter sw) : base(sw)
{
m_sw = sw;
Formatting = Formatting.Indented;
}
public RestXmlWriter(TextWriter textWriter) : base(textWriter)
{
}
public RestXmlWriter(Stream stream)
: this(stream, Encoding.UTF8)
{
}
public RestXmlWriter(Stream stream, Encoding enc) : base(stream, enc)
{
}
public override void WriteStartDocument()
{
}
public override void WriteStartDocument(bool standalone)
{
}
public override string ToString()
{
Flush();
Close();
return m_sw.ToString();
}
}
}

View File

@ -0,0 +1,276 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Open Simulator Export/Import XML schema
August 2008
</xsd:documentation>
</xsd:annotation>
<!-- WARNING!!!
This is currently a draft, it does not reflect
what is exported, nor what will be understood
on import. It is included as a working document
and this comment will be removed at such time as
the schema corresponds to reality.
-->
<!--
REST-related information
Inventory data is always framed by an
inventory element. Consists of zero or
more elements representing either folders
or items within those folders. The inventory
element represents the "real" root folder.
-->
<xsd:element name="inventory" type="inventory_ct" />
<!--
The inventory complex type is just an arbitrary
sequence of folders and items. In reality it is
typically just folders. Both item and folder
have corresponding complex types. It is distinct
from folders insofar as it has no other defining
attributes.
-->
<xsd:complexType name="inventory_ct">
<xsd:element name="folder" type="folder_ct" maxOccurs="unbounded"/>
<xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
</xsd:complexType>
<xsd:complexType name="folder_ct">
<xsd:attribute name="UUID" type="uuid_st" />
<xsd:attribute name="name" type="name_st" />
<xsd:attribute name="type" type="folder_type_st" />
<xsd:attribute name="description" type="xsd:string" /> <!-- added -->
<xsd:attribute name="version" type="unsignedShort" />
<xsd:attribute name="owner" type="uuid_st" />
<xsd:attribute name="creator" type="uuid_st" /> <!-- added -->
<xsd:attribute name="creationdate" type="date_st" /> <!-- added -->
<xsd:attribute name="parent" type="uuid_st" />
<xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" /> <!-- added -->
<xsd:element name="folder" type="folder_ct" maxOccurs="unbounded" />
<xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
</xsd:complexType>
<xsd:complexType name="item_ct">
<xsd:attribute name="UUID" type="uuid_st" />
<xsd:attribute name="name" type="name_st" />
<xsd:attribute name="type" type="inventory_type_st" />
<xsd:attribute name="description" type="xsd:string" />
<xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
<xsd:attribute name="owner" type="uuid_st" />
<xsd:attribute name="creator" type="uuid_st" />
<xsd:attribute name="creationdate" type="date_st" />
<xsd:attribute name="folder" type="uuid_st" />
<xsd:attribute name="groupid" type="uuid_st" />
<xsd:attribute name="groupowned" type="xsd:boolean" />
<xsd:attribute name="saletype" type="sale_st" />
<xsd:attribute name="saleprice" type="xsd:decimal" />
<xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" />
</xsd:complexType>
<xsd:complexType name="asset_ct">
<xsd:attribute name="UUID" type="uuid_st" />
<xsd:attribute name="name" type="name_st" />
<xsd:attribute name="type" type="asset_type_st" />
<xsd:attribute name="description" type="xsd:string" />
<xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
<xsd:attribute name="owner" type="uuid_st" />
<xsd:attribute name="creator" type="uuid_st" />
<xsd:attribute name="creationdate" type="date_st" />
<xsd:attribute name="temporary" type="xsd:boolean" />
<xsd:attribute name="local" type="xsd:boolean" />
<xsd:attribute name="inline" type="xsd:boolean" />
</xsd:complexType>
<!-- Constrained Simple Data types -->
<!--
We need to specify name as a simple type because on
some platforms it is constrained by a certain length
limitation. For completeness we indicate that whitespace
should be preserved exactly as specified.
-->
<xsd:simpleType name="name_st">
<xsd:restriction base="xsd:string">
<whiteSpace value="preserve" />
<minLength value="0" />
<maxLength value="64" />
</xsd:restriction>
</xsd:simpleType>
<!--
Type information in the folder is meant to indicate
the preferred asset type for this folder. As such, that
currently corresponds to the type values allowed for
assets, however that is not mandated, so for
now at least I'll represent this as a distinct
enumeration.
This seems inappropriate; it seems like the folder's
content should reflect the InventoryType classifications
rather than the asset types.
-->
<xsd:simpleType name="folder_type_st">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Texture" />
<xsd:enumeration value="Sound" />
<xsd:enumeration value="CallingCard" />
<xsd:enumeration value="Landmark" />
<xsd:enumeration value="Script" />
<xsd:enumeration value="Clothing" />
<xsd:enumeration value="Object" />
<xsd:enumeration value="Notecard" />
<xsd:enumeration value="LSLText" />
<xsd:enumeration value="LSLByteCode" />
<xsd:enumeration value="TextureTGA" />
<xsd:enumeration value="BodyPart" />
<xsd:enumeration value="SoundWAV" />
<xsd:enumeration value="ImageTGA" />
<xsd:enumeration value="ImageJPEG" />
<xsd:enumeration value="Animation" />
<xsd:enumeration value="Gesture" />
<xsd:enumeration value="Simstate" />
<xsd:enumeration value="Unknown" />
<xsd:enumeration value="LostAndFoundFolder" />
<xsd:enumeration value="SnapshotFolder" />
<xsd:enumeration value="TrashFolder" />
<xsd:enumeration value="Folder" />
<xsd:enumeration value="RootFolder" />
</xsd:restriction>
</xsd:simpleType>
<!--
Inventory item type designates an asset class, rather
than a specific asset type. For example, "SnapShot"
might include a number of asset types such as JPEG,
TGA, etc.. This is not a consistent interpretation,
classifications such as LostAndFound are meta-types
relative to asset classes.
These types should be abstract and not be tied to a
specific platform. A world's import facility should be
responsible for mapping these to meaningful internal
representations.
These types were based on information in:
libsecondlife/InventoryManager.cs
-->
<xsd:simpleType name="inventory_type_st">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Texture" />
<xsd:enumeration value="Sound" />
<xsd:enumeration value="CallingCard" />
<xsd:enumeration value="Landmark" />
<xsd:enumeration value="Script" />
<xsd:enumeration value="Clothing" />
<xsd:enumeration value="Object" />
<xsd:enumeration value="Notecard" />
<xsd:enumeration value="LSL" />
<xsd:enumeration value="LSLBytecode" />
<xsd:enumeration value="TextureTGA" />
<xsd:enumeration value="BodyPart" />
<xsd:enumeration value="Snapshot" />
<xsd:enumeration value="Attachment" />
<xsd:enumeration value="Wearable" />
<xsd:enumeration value="Animation" />
<xsd:enumeration value="Gesture" />
<xsd:enumeration value="Folder" />
<xsd:enumeration value="Unknown" />
<xsd:enumeration value="LostAndFound" />
<xsd:enumeration value="Trash" />
<xsd:enumeration value="Root" />
</xsd:restriction>
</xsd:simpleType>
<!--
The asset types seem to be even more disarrayed than
the inventory types. It seems to be little more than
a reiteration of the inventory type information,
which adds little or nothing to the overall data
model.
Of course, given that these are drawn from the
libsecondlife definitions, we aren't at liberty to
simply redefine them in place. But the XML definitions
here could be made more useful.
These types were based on information in:
libsecondlife/AssetManager.cs
-->
<xsd:simpleType name="asset_type_st">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Texture" />
<xsd:enumeration value="Sound" />
<xsd:enumeration value="CallingCard" />
<xsd:enumeration value="Landmark" />
<xsd:enumeration value="Script" />
<xsd:enumeration value="Clothing" />
<xsd:enumeration value="Object" />
<xsd:enumeration value="Notecard" />
<xsd:enumeration value="LSLText" />
<xsd:enumeration value="LSLByteCode" />
<xsd:enumeration value="TextureTGA" />
<xsd:enumeration value="BodyPart" />
<xsd:enumeration value="SoundWAV" />
<xsd:enumeration value="ImageTGA" />
<xsd:enumeration value="ImageJPEG" />
<xsd:enumeration value="Animation" />
<xsd:enumeration value="Gesture" />
<xsd:enumeration value="Simstate" />
<xsd:enumeration value="Unknown" />
<xsd:enumeration value="LostAndFoundFolder" />
<xsd:enumeration value="SnapshotFolder" />
<xsd:enumeration value="TrashFolder" />
<xsd:enumeration value="Folder" />
<xsd:enumeration value="RootFolder" />
</xsd:restriction>
</xsd:simpleType>
<!-- This is describing the apparent form of a UUID. If
we ever want a more metaphysical definition we'll
need to add to it.
-->
<xsd:simpleType name="uuid_st">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"/>
</xsd:restriction>
</xsd:simpleType>
<!-- This constrains the date representation. Currently
it is simply an integer representing the elapsed
?? since ??.
-->
<xsd:simpleType name="date_st">
<xsd:restriction base="xsd:positiveInteger">
</xsd:restriction>
</xsd:simpleType>
<!-- This constrains the representation of sale price.
Currently it is a simple decimal with no unit
specified.
Issues: interoperability.
-->
<xsd:simpleType name="sale_st">
<xsd:restriction base="xsd:decimal">
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

View File

@ -28,10 +28,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
using System.Threading;
using log4net;
using Nini.Config;
using OpenMetaverse;
@ -50,9 +48,10 @@ namespace OpenSim.Framework.Capabilities
/// </summary>
public delegate IClientAPI GetClientDelegate(UUID agentID);
public class Caps : IDisposable
public class Caps
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// private static readonly ILog m_log =
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private string m_httpListenerHostName;
private uint m_httpListenPort;
@ -64,16 +63,11 @@ namespace OpenSim.Framework.Capabilities
public string CapsObjectPath { get { return m_capsObjectPath; } }
private CapsHandlers m_capsHandlers;
private ConcurrentDictionary<string, PollServiceEventArgs> m_pollServiceHandlers
= new ConcurrentDictionary<string, PollServiceEventArgs>();
private Dictionary<string, string> m_externalCapsHandlers = new Dictionary<string, string>();
private Dictionary<string, string> m_externalCapsHandlers;
private IHttpServer m_httpListener;
private UUID m_agentID;
private string m_regionName;
private ManualResetEvent m_capsActive = new ManualResetEvent(false);
public UUID AgentID
{
@ -120,19 +114,6 @@ namespace OpenSim.Framework.Capabilities
get { return m_externalCapsHandlers; }
}
[Flags]
public enum CapsFlags:uint
{
None = 0,
SentSeeds = 1,
ObjectAnim = 0x100,
WLEnv = 0x200,
AdvEnv = 0x400
}
public CapsFlags Flags { get; set;}
public Caps(IHttpServer httpServer, string httpListen, uint httpPort, string capsPath,
UUID agent, string regionName)
{
@ -150,37 +131,9 @@ namespace OpenSim.Framework.Capabilities
}
m_agentID = agent;
m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort);
m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort, (httpServer == null) ? false : httpServer.UseSSL);
m_externalCapsHandlers = new Dictionary<string, string>();
m_regionName = regionName;
Flags = CapsFlags.None;
m_capsActive.Reset();
}
~Caps()
{
Flags = CapsFlags.None;
if (m_capsActive!= null)
{
m_capsActive.Dispose();
m_capsActive = null;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
Flags = CapsFlags.None;
if (m_capsActive != null)
{
DeregisterHandlers();
m_capsActive.Dispose();
m_capsActive = null;
}
}
/// <summary>
@ -190,45 +143,8 @@ namespace OpenSim.Framework.Capabilities
/// <param name="handler"></param>
public void RegisterHandler(string capName, IRequestHandler handler)
{
//m_log.DebugFormat("[CAPS]: Registering handler for \"{0}\": path {1}", capName, handler.Path);
m_capsHandlers[capName] = handler;
}
public void RegisterSimpleHandler(string capName, ISimpleStreamHandler handler, bool addToListener = true)
{
//m_log.DebugFormat("[CAPS]: Registering handler for \"{0}\": path {1}", capName, handler.Path);
m_capsHandlers.AddSimpleHandler(capName, handler, addToListener);
}
public void RegisterPollHandler(string capName, PollServiceEventArgs pollServiceHandler)
{
// m_log.DebugFormat(
// "[CAPS]: Registering handler with name {0}, url {1} for {2}",
// capName, pollServiceHandler.Url, m_agentID, m_regionName);
if(!m_pollServiceHandlers.TryAdd(capName, pollServiceHandler))
{
m_log.ErrorFormat(
"[CAPS]: Handler with name {0} already registered (ulr {1}, agent {2}, region {3}",
capName, pollServiceHandler.Url, m_agentID, m_regionName);
return;
}
m_httpListener.AddPollServiceHTTPHandler(pollServiceHandler);
// uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
// string protocol = "http";
// string hostName = m_httpListenerHostName;
//
// if (MainServer.Instance.UseSSL)
// {
// hostName = MainServer.Instance.SSLCommonName;
// port = MainServer.Instance.SSLPort;
// protocol = "https";
// }
// RegisterHandler(
// capName, String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, pollServiceHandler.Url));
}
/// <summary>
@ -247,80 +163,13 @@ namespace OpenSim.Framework.Capabilities
/// </summary>
public void DeregisterHandlers()
{
foreach (string capsName in m_capsHandlers.Caps)
if (m_capsHandlers != null)
{
m_capsHandlers.Remove(capsName);
}
foreach (PollServiceEventArgs handler in m_pollServiceHandlers.Values)
{
m_httpListener.RemovePollServiceHTTPHandler(handler.Url);
}
m_pollServiceHandlers.Clear();
}
public bool TryGetPollHandler(string name, out PollServiceEventArgs pollHandler)
{
return m_pollServiceHandlers.TryGetValue(name, out pollHandler);
}
public Dictionary<string, PollServiceEventArgs> GetPollHandlers()
{
return new Dictionary<string, PollServiceEventArgs>(m_pollServiceHandlers);
}
/// <summary>
/// Return an LLSD-serializable Hashtable describing the
/// capabilities and their handler details.
/// </summary>
/// <param name="excludeSeed">If true, then exclude the seed cap.</param>
public Hashtable GetCapsDetails(bool excludeSeed, List<string> requestedCaps)
{
Hashtable caps = CapsHandlers.GetCapsDetails(excludeSeed, requestedCaps);
lock (m_pollServiceHandlers)
{
foreach (KeyValuePair <string, PollServiceEventArgs> kvp in m_pollServiceHandlers)
foreach (string capsName in m_capsHandlers.Caps)
{
if (!requestedCaps.Contains(kvp.Key))
continue;
string hostName = m_httpListenerHostName;
uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
string protocol = "http";
if (MainServer.Instance.UseSSL)
{
hostName = MainServer.Instance.SSLCommonName;
port = MainServer.Instance.SSLPort;
protocol = "https";
}
caps[kvp.Key] = string.Format("{0}://{1}:{2}{3}", protocol, hostName, port, kvp.Value.Url);
m_capsHandlers.Remove(capsName);
}
}
// Add the external too
foreach (KeyValuePair<string, string> kvp in ExternalCapsHandlers)
{
if (!requestedCaps.Contains(kvp.Key))
continue;
caps[kvp.Key] = kvp.Value;
}
return caps;
}
public void Activate()
{
m_capsActive.Set();
}
public bool WaitForActivation()
{
// Wait for 30s. If that elapses, return false and run without caps
return m_capsActive.WaitOne(120000);
}
}
}
}

View File

@ -27,7 +27,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
@ -40,8 +39,7 @@ namespace OpenSim.Framework.Capabilities
/// </summary>
public class CapsHandlers
{
private Dictionary<string, IRequestHandler> m_capsHandlers = new Dictionary<string, IRequestHandler>();
private ConcurrentDictionary<string, ISimpleStreamHandler> m_capsSimpleHandlers = new ConcurrentDictionary<string, ISimpleStreamHandler>();
private Dictionary <string, IRequestHandler> m_capsHandlers = new Dictionary<string, IRequestHandler>();
private IHttpServer m_httpListener;
private string m_httpListenerHostName;
private uint m_httpListenerPort;
@ -55,15 +53,31 @@ namespace OpenSim.Framework.Capabilities
/// <param name="httpListener">base HTTP server</param>
/// <param name="httpListenerHostname">host name of the HTTP server</param>
/// <param name="httpListenerPort">HTTP port</param>
public CapsHandlers(IHttpServer httpListener, string httpListenerHostname, uint httpListenerPort)
{
public CapsHandlers(BaseHttpServer httpListener, string httpListenerHostname, uint httpListenerPort)
: this(httpListener,httpListenerHostname,httpListenerPort, false)
{
}
/// <summary></summary>
/// CapsHandlers is a cap handler container but also takes
/// care of adding and removing cap handlers to and from the
/// supplied BaseHttpServer.
/// </summary>
/// <param name="httpListener">base HTTP server</param>
/// <param name="httpListenerHostname">host name of the HTTP
/// server</param>
/// <param name="httpListenerPort">HTTP port</param>
public CapsHandlers(IHttpServer httpListener, string httpListenerHostname, uint httpListenerPort, bool https)
{
m_httpListener = httpListener;
m_httpListenerHostName = httpListenerHostname;
m_httpListenerPort = httpListenerPort;
if (httpListener != null && httpListener.UseSSL)
m_useSSL = true;
else
m_useSSL = false;
m_useSSL = https;
if (httpListener != null && m_useSSL)
{
m_httpListenerHostName = httpListener.SSLCommonName;
m_httpListenerPort = httpListener.SSLPort;
}
}
/// <summary>
@ -75,35 +89,16 @@ namespace OpenSim.Framework.Capabilities
{
lock (m_capsHandlers)
{
if(m_capsHandlers.ContainsKey(capsName))
{
m_httpListener.RemoveStreamHandler("POST", m_capsHandlers[capsName].Path);
m_httpListener.RemoveStreamHandler("PUT", m_capsHandlers[capsName].Path);
m_httpListener.RemoveStreamHandler("GET", m_capsHandlers[capsName].Path);
m_httpListener.RemoveStreamHandler("DELETE", m_capsHandlers[capsName].Path);
m_capsHandlers.Remove(capsName);
}
m_httpListener.RemoveStreamHandler("POST", m_capsHandlers[capsName].Path);
m_httpListener.RemoveStreamHandler("GET", m_capsHandlers[capsName].Path);
m_capsHandlers.Remove(capsName);
}
if(m_capsSimpleHandlers.TryRemove(capsName, out ISimpleStreamHandler hdr))
{
m_httpListener.RemoveSimpleStreamHandler(hdr.Path);
}
}
public void AddSimpleHandler(string capName, ISimpleStreamHandler handler, bool addToListener = true)
{
if(ContainsCap(capName))
Remove(capName);
if(m_capsSimpleHandlers.TryAdd(capName, handler) && addToListener)
m_httpListener.AddSimpleStreamHandler(handler);
}
public bool ContainsCap(string cap)
{
lock (m_capsHandlers)
if (m_capsHandlers.ContainsKey(cap))
return true;
return m_capsSimpleHandlers.ContainsKey(cap);
return m_capsHandlers.ContainsKey(cap);
}
/// <summary>
@ -130,14 +125,11 @@ namespace OpenSim.Framework.Capabilities
if (m_capsHandlers.ContainsKey(idx))
{
m_httpListener.RemoveStreamHandler("POST", m_capsHandlers[idx].Path);
m_httpListener.RemoveStreamHandler("PUT", m_capsHandlers[idx].Path);
m_httpListener.RemoveStreamHandler("GET", m_capsHandlers[idx].Path);
m_httpListener.RemoveStreamHandler("DELETE", m_capsHandlers[idx].Path);
m_capsHandlers.Remove(idx);
}
if (null == value) return;
m_capsHandlers[idx] = value;
m_httpListener.AddStreamHandler(value);
}
@ -154,9 +146,8 @@ namespace OpenSim.Framework.Capabilities
{
lock (m_capsHandlers)
{
string[] __keys = new string[m_capsHandlers.Keys.Count + m_capsSimpleHandlers.Keys.Count];
string[] __keys = new string[m_capsHandlers.Keys.Count];
m_capsHandlers.Keys.CopyTo(__keys, 0);
m_capsSimpleHandlers.Keys.CopyTo(__keys, m_capsHandlers.Keys.Count);
return __keys;
}
}
@ -167,58 +158,28 @@ namespace OpenSim.Framework.Capabilities
/// capabilities and their handler details.
/// </summary>
/// <param name="excludeSeed">If true, then exclude the seed cap.</param>
public Hashtable GetCapsDetails(bool excludeSeed, List<string> requestedCaps)
public Hashtable GetCapsDetails(bool excludeSeed)
{
Hashtable caps = new Hashtable();
string protocol = "http://";
if (m_useSSL)
protocol = "https://";
string protocol = m_useSSL ? "https://" : "http://";
string baseUrl = protocol + m_httpListenerHostName + ":" + m_httpListenerPort.ToString();
if (requestedCaps == null)
{
lock (m_capsHandlers)
{
foreach (KeyValuePair<string, ISimpleStreamHandler> kvp in m_capsSimpleHandlers)
caps[kvp.Key] = baseUrl + kvp.Value.Path;
foreach (KeyValuePair<string, IRequestHandler> kvp in m_capsHandlers)
caps[kvp.Key] = baseUrl + kvp.Value.Path;
}
return caps;
}
lock (m_capsHandlers)
{
for(int i = 0; i < requestedCaps.Count; ++i)
foreach (string capsName in m_capsHandlers.Keys)
{
string capsName = requestedCaps[i];
if (excludeSeed && "SEED" == capsName)
continue;
if (m_capsSimpleHandlers.TryGetValue(capsName, out ISimpleStreamHandler shdr))
{
caps[capsName] = baseUrl + shdr.Path;
continue;
}
if (m_capsHandlers.TryGetValue(capsName, out IRequestHandler chdr))
{
caps[capsName] = baseUrl + chdr.Path;
}
caps[capsName] = baseUrl + m_capsHandlers[capsName].Path;
}
}
return caps;
}
/// <summary>
/// Returns a copy of the dictionary of all the HTTP cap handlers
/// </summary>
/// <returns>
/// The dictionary copy. The key is the capability name, the value is the HTTP handler.
/// </returns>
public Dictionary<string, IRequestHandler> GetCapsHandlers()
{
lock (m_capsHandlers)
return new Dictionary<string, IRequestHandler>(m_capsHandlers);
}
}
}

View File

@ -1,449 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Capabilities;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Services.Interfaces;
using OSDMap = OpenMetaverse.StructuredData.OSDMap;
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
namespace OpenSim.Capabilities.Handlers
{
public class FetchInvDescHandler
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static byte[] EmptyResponse = Util.UTF8NBGetbytes("<llsd><map><key>folders</key><array /></map></llsd>");
private IInventoryService m_InventoryService;
private ILibraryService m_LibraryService;
private IScene m_Scene;
public FetchInvDescHandler(IInventoryService invService, ILibraryService libService, IScene s)
{
m_InventoryService = invService;
m_LibraryService = libService;
m_Scene = s;
}
public void FetchInventoryDescendentsRequest(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse, ExpiringKey<UUID> BadRequests)
{
//m_log.DebugFormat("[XXX]: FetchInventoryDescendentsRequest in {0}, {1}", (m_Scene == null) ? "none" : m_Scene.Name, request);
List<LLSDFetchInventoryDescendents> folders = null;
List<UUID> bad_folders = new List<UUID>();
try
{
OSDArray foldersrequested = null;
OSD tmp = OSDParser.DeserializeLLSDXml(httpRequest.InputStream);
OSDMap map = (OSDMap)tmp;
if(map.TryGetValue("folders", out tmp) && tmp is OSDArray)
foldersrequested = tmp as OSDArray;
if (foldersrequested == null || foldersrequested.Count == 0)
{
httpResponse.RawBuffer = EmptyResponse;
return;
}
folders = new List<LLSDFetchInventoryDescendents>(foldersrequested.Count);
for (int i = 0; i < foldersrequested.Count; i++)
{
OSDMap mfolder = foldersrequested[i] as OSDMap;
UUID id = mfolder["folder_id"].AsUUID();
if(BadRequests.ContainsKey(id))
{
bad_folders.Add(id);
}
else
{
LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents();
try
{
llsdRequest.folder_id = id;
llsdRequest.owner_id = mfolder["owner_id"].AsUUID();
llsdRequest.sort_order = mfolder["sort_order"].AsInteger();
llsdRequest.fetch_folders = mfolder["fetch_folders"].AsBoolean();
llsdRequest.fetch_items = mfolder["fetch_items"].AsBoolean();
}
catch (Exception e)
{
m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e.Message);
continue;
}
folders.Add(llsdRequest);
}
}
foldersrequested = null;
tmp = null;
}
catch (Exception e)
{
m_log.ErrorFormat("[FETCH INV DESC]: fail parsing request: {0}", e.Message);
httpResponse.RawBuffer = EmptyResponse;
return;
}
if (folders == null || folders.Count == 0)
{
if(bad_folders.Count == 0)
{
httpResponse.RawBuffer = EmptyResponse;
return;
}
StringBuilder sb = osStringBuilderCache.Acquire();
sb.Append("<llsd><map><key>folders</key><array /></map><map><key>bad_folders</key><array>");
foreach (UUID bad in bad_folders)
{
sb.Append("<map><key>folder_id</key><uuid>");
sb.Append(bad.ToString());
sb.Append("</uuid><key>error</key><string>Unknown</string></map>");
}
sb.Append("</array></map></llsd>");
httpResponse.RawBuffer = Util.UTF8NBGetbytes(osStringBuilderCache.GetStringAndRelease(sb));
}
int total_folders = 0;
int total_items = 0;
List<InventoryCollection> invcollSet = Fetch(folders, bad_folders, ref total_folders, ref total_items);
//m_log.DebugFormat("[XXX]: Got {0} folders from a request of {1}", invcollSet.Count, folders.Count);
int invcollSetCount = 0;
if (invcollSet != null)
invcollSetCount = invcollSet.Count;
int mem = 8192 + ((256 * invcollSetCount +
384 * total_folders +
1024 * total_items +
128 * bad_folders.Count) & 0x7ffff000);
StringBuilder lastresponse = new StringBuilder(mem);
lastresponse.Append("<llsd>");
if (invcollSetCount > 0)
{
lastresponse.Append("<map><key>folders</key><array>");
int i = 0;
InventoryCollection thiscoll;
for (i = 0; i < invcollSetCount; i++)
{
thiscoll = invcollSet[i];
invcollSet[i] = null;
LLSDxmlEncode.AddMap(lastresponse);
LLSDxmlEncode.AddElem("agent_id", thiscoll.OwnerID, lastresponse);
LLSDxmlEncode.AddElem("descendents", thiscoll.Descendents, lastresponse);
LLSDxmlEncode.AddElem("folder_id", thiscoll.FolderID, lastresponse);
if (thiscoll.Folders == null || thiscoll.Folders.Count == 0)
LLSDxmlEncode.AddEmptyArray("categories", lastresponse);
else
{
LLSDxmlEncode.AddArray("categories", lastresponse);
foreach (InventoryFolderBase invFolder in thiscoll.Folders)
{
LLSDxmlEncode.AddMap(lastresponse);
LLSDxmlEncode.AddElem("folder_id", invFolder.ID, lastresponse);
LLSDxmlEncode.AddElem("parent_id", invFolder.ParentID, lastresponse);
LLSDxmlEncode.AddElem("name", invFolder.Name, lastresponse);
LLSDxmlEncode.AddElem("type", invFolder.Type, lastresponse);
LLSDxmlEncode.AddElem("preferred_type", (int)-1, lastresponse);
LLSDxmlEncode.AddElem("version", invFolder.Version, lastresponse);
LLSDxmlEncode.AddEndMap(lastresponse);
}
LLSDxmlEncode.AddEndArray(lastresponse);
}
if (thiscoll.Items == null || thiscoll.Items.Count == 0)
LLSDxmlEncode.AddEmptyArray("items", lastresponse);
else
{
LLSDxmlEncode.AddArray("items", lastresponse);
foreach (InventoryItemBase invItem in thiscoll.Items)
{
invItem.ToLLSDxml(lastresponse);
}
LLSDxmlEncode.AddEndArray(lastresponse);
}
LLSDxmlEncode.AddElem("owner_id", thiscoll.OwnerID, lastresponse);
LLSDxmlEncode.AddElem("version", thiscoll.Version, lastresponse);
LLSDxmlEncode.AddEndMap(lastresponse);
invcollSet[i] = null;
}
lastresponse.Append("</array></map>");
thiscoll = null;
}
else
{
lastresponse.Append("<map><key>folders</key><array /></map>");
}
//m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Bad folders {0}", string.Join(", ", bad_folders));
if (bad_folders.Count > 0)
{
lastresponse.Append("<map><key>bad_folders</key><array>");
foreach (UUID bad in bad_folders)
{
BadRequests.Add(bad);
lastresponse.Append("<map><key>folder_id</key><uuid>");
lastresponse.Append(bad.ToString());
lastresponse.Append("</uuid><key>error</key><string>Unknown</string></map>");
}
lastresponse.Append("</array></map>");
}
lastresponse.Append("</llsd>");
httpResponse.RawBuffer = Util.UTF8NBGetbytes(lastresponse.ToString());
}
private void AddLibraryFolders(List<LLSDFetchInventoryDescendents> libFolders, List<InventoryCollection> result, ref int total_folders, ref int total_items)
{
InventoryFolderImpl fold;
if (m_LibraryService == null || m_LibraryService.LibraryRootFolder == null)
return;
foreach (LLSDFetchInventoryDescendents f in libFolders)
{
if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(f.folder_id)) != null)
{
InventoryCollection Collection = new InventoryCollection();
// ret.Collection.Folders = new List<InventoryFolderBase>();
Collection.Folders = fold.RequestListOfFolders();
Collection.Items = fold.RequestListOfItems();
Collection.OwnerID = m_LibraryService.LibraryRootFolder.Owner;
Collection.FolderID = f.folder_id;
Collection.Version = fold.Version;
Collection.Descendents = Collection.Items.Count + Collection.Folders.Count;
total_folders += Collection.Folders.Count;
total_items += Collection.Items.Count;
result.Add(Collection);
//m_log.DebugFormat("[XXX]: Added libfolder {0} ({1}) {2}", ret.Collection.FolderID, ret.Collection.OwnerID);
}
}
}
private List<InventoryCollection> Fetch(List<LLSDFetchInventoryDescendents> fetchFolders, List<UUID> bad_folders, ref int total_folders, ref int total_items)
{
//m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Fetching {0} folders for owner {1}", fetchFolders.Count, fetchFolders[0].owner_id);
// FIXME MAYBE: We're not handling sortOrder!
List<InventoryCollection> result = new List<InventoryCollection>(32);
List<LLSDFetchInventoryDescendents> libFolders = new List<LLSDFetchInventoryDescendents>(32);
List<LLSDFetchInventoryDescendents> otherFolders = new List<LLSDFetchInventoryDescendents>(32);
HashSet<UUID> libIDs = new HashSet<UUID>();
HashSet<UUID> otherIDs = new HashSet<UUID>();
bool dolib = (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null);
UUID libOwner = UUID.Zero;
if(dolib)
libOwner = m_LibraryService.LibraryRootFolder.Owner;
// Filter folder Zero right here. Some viewers (Firestorm) send request for folder Zero, which doesn't make sense
// and can kill the sim (all root folders have parent_id Zero)
// send something.
bool doneZeroID = false;
foreach(LLSDFetchInventoryDescendents f in fetchFolders)
{
if (f.folder_id == UUID.Zero)
{
if(doneZeroID)
continue;
doneZeroID = true;
InventoryCollection Collection = new InventoryCollection();
Collection.OwnerID = f.owner_id;
Collection.Version = 0;
Collection.FolderID = f.folder_id;
Collection.Descendents = 0;
result.Add(Collection);
continue;
}
if(dolib && f.owner_id == libOwner)
{
if(libIDs.Contains(f.folder_id))
continue;
libIDs.Add(f.folder_id);
libFolders.Add(f);
continue;
}
if(otherIDs.Contains(f.folder_id))
continue;
otherIDs.Add(f.folder_id);
otherFolders.Add(f);
}
fetchFolders.Clear();
if(otherFolders.Count > 0)
{
int i = 0;
//m_log.DebugFormat("[XXX]: {0}", string.Join(",", fids));
InventoryCollection[] fetchedContents = m_InventoryService.GetMultipleFoldersContent(otherFolders[0].owner_id, otherIDs.ToArray());
if (fetchedContents == null)
return null;
if (fetchedContents.Length == 0)
{
foreach (LLSDFetchInventoryDescendents freq in otherFolders)
BadFolder(freq, null, bad_folders);
}
else
{
i = 0;
// Do some post-processing. May need to fetch more from inv server for links
foreach (InventoryCollection contents in fetchedContents)
{
// Find the original request
LLSDFetchInventoryDescendents freq = otherFolders[i];
otherFolders[i]=null;
i++;
if (BadFolder(freq, contents, bad_folders))
continue;
if(!freq.fetch_folders)
contents.Folders.Clear();
if(!freq.fetch_items)
contents.Items.Clear();
contents.Descendents = contents.Items.Count + contents.Folders.Count;
// Next: link management
ProcessLinks(freq, contents);
total_folders += contents.Folders.Count;
total_items += contents.Items.Count;
result.Add(contents);
}
}
}
if(dolib && libFolders.Count > 0)
{
AddLibraryFolders(libFolders, result, ref total_folders, ref total_items);
}
return result;
}
private bool BadFolder(LLSDFetchInventoryDescendents freq, InventoryCollection contents, List<UUID> bad_folders)
{
if (contents == null)
{
bad_folders.Add(freq.folder_id);
return true;
}
// The inventory server isn't sending FolderID in the collection...
// Must fetch it individually
if (contents.FolderID == UUID.Zero)
{
InventoryFolderBase containingFolder = m_InventoryService.GetFolder(freq.owner_id, freq.folder_id);
if (containingFolder == null)
{
m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Unable to fetch folder {0}", freq.folder_id);
bad_folders.Add(freq.folder_id);
return true;
}
contents.FolderID = containingFolder.ID;
contents.OwnerID = containingFolder.Owner;
contents.Version = containingFolder.Version;
}
return false;
}
private void ProcessLinks(LLSDFetchInventoryDescendents freq, InventoryCollection contents)
{
if (contents.Items == null || contents.Items.Count == 0)
return;
// viewers are lasy and want a copy of the linked item sent before the link to it
// look for item links
List<UUID> itemIDs = new List<UUID>();
foreach (InventoryItemBase item in contents.Items)
{
//m_log.DebugFormat("[XXX]: {0} {1}", item.Name, item.AssetType);
if (item.AssetType == (int)AssetType.Link)
itemIDs.Add(item.AssetID);
}
// get the linked if any
if (itemIDs.Count > 0)
{
InventoryItemBase[] linked = m_InventoryService.GetMultipleItems(freq.owner_id, itemIDs.ToArray());
if (linked != null)
{
List<InventoryItemBase> linkedItems = new List<InventoryItemBase>(linked.Length);
// check for broken
foreach (InventoryItemBase linkedItem in linked)
{
// Take care of genuinely broken links where the target doesn't exist
// HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
// but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
// rather than having to keep track of every folder requested in the recursion.
if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
{
linkedItems.Add(linkedItem);
//m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Added {0} {1} {2}", linkedItem.Name, linkedItem.AssetType, linkedItem.Folder);
}
}
// insert them
if(linkedItems.Count > 0)
contents.Items.InsertRange(0, linkedItems);
}
}
}
}
}

View File

@ -1,166 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Net;
using System.Reflection;
using System.Text;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Services.Interfaces;
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
using OSDMap = OpenMetaverse.StructuredData.OSDMap;
using log4net;
namespace OpenSim.Capabilities.Handlers
{
public class FetchInventory2Handler
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IInventoryService m_inventoryService;
private UUID m_agentID;
public FetchInventory2Handler(IInventoryService invService, UUID agentId)
{
m_inventoryService = invService;
m_agentID = agentId;
}
public string FetchInventoryRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
//m_log.DebugFormat("[FETCH INVENTORY HANDLER]: Received FetchInventory capability request {0}", request);
OSDMap requestmap = (OSDMap)OSDParser.DeserializeLLSDXml(Utils.StringToBytes(request));
OSDArray itemsRequested = (OSDArray)requestmap["items"];
UUID[] itemIDs = new UUID[itemsRequested.Count];
int i = 0;
foreach (OSDMap osdItemId in itemsRequested)
{
itemIDs[i++] = osdItemId["item_id"].AsUUID();
}
InventoryItemBase[] items = null;
if (m_agentID != UUID.Zero)
{
items = m_inventoryService.GetMultipleItems(m_agentID, itemIDs);
}
else
{
items = new InventoryItemBase[itemsRequested.Count];
foreach (UUID id in itemIDs)
items[i++] = m_inventoryService.GetItem(UUID.Zero, id);
}
StringBuilder lsl = LLSDxmlEncode.Start(4096);
LLSDxmlEncode.AddMap(lsl);
if(m_agentID == UUID.Zero && items.Length > 0)
LLSDxmlEncode.AddElem("agent_id", items[0].Owner, lsl);
else
LLSDxmlEncode.AddElem("agent_id", m_agentID, lsl);
if(items == null || items.Length == 0)
{
LLSDxmlEncode.AddEmptyArray("items", lsl);
}
else
{
LLSDxmlEncode.AddArray("items", lsl);
foreach (InventoryItemBase item in items)
{
if (item != null)
item.ToLLSDxml(lsl, 0xff);
}
LLSDxmlEncode.AddEndArray(lsl);
}
LLSDxmlEncode.AddEndMap(lsl);
return LLSDxmlEncode.End(lsl);
}
public void FetchInventorySimpleRequest(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse, OSDMap requestmap, ExpiringKey<UUID> BadRequests)
{
//m_log.DebugFormat("[FETCH INVENTORY HANDLER]: Received FetchInventory capability request {0}", request);
if(BadRequests == null)
{
httpResponse.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
OSDArray itemsRequested = (OSDArray)requestmap["items"];
UUID[] itemIDs = new UUID[itemsRequested.Count];
int i = 0;
foreach (OSDMap osdItemId in itemsRequested)
{
UUID id = osdItemId["item_id"].AsUUID();
if(!BadRequests.ContainsKey(id))
itemIDs[i++] = id;
}
InventoryItemBase[] items = null;
try
{
// badrequests still not filled
items = m_inventoryService.GetMultipleItems(m_agentID, itemIDs);
}
catch{ }
StringBuilder lsl = LLSDxmlEncode.Start(4096);
LLSDxmlEncode.AddMap(lsl);
LLSDxmlEncode.AddElem("agent_id", m_agentID, lsl);
if (items == null || items.Length == 0)
{
LLSDxmlEncode.AddEmptyArray("items", lsl);
}
else
{
LLSDxmlEncode.AddArray("items", lsl);
foreach (InventoryItemBase item in items)
{
if (item != null)
item.ToLLSDxml(lsl, 0xff);
}
LLSDxmlEncode.AddEndArray(lsl);
}
LLSDxmlEncode.AddEndMap(lsl);
httpResponse.RawBuffer = Util.UTF8.GetBytes(LLSDxmlEncode.End(lsl));
httpResponse.StatusCode = (int)HttpStatusCode.OK;
}
}
}

View File

@ -1,170 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using log4net;
using log4net.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Capabilities.Handlers;
using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
namespace OpenSim.Capabilities.Handlers.FetchInventory.Tests
{
[TestFixture]
public class FetchInventory2HandlerTests : OpenSimTestCase
{
private UUID m_userID = UUID.Random();
private Scene m_scene;
private UUID m_rootFolderID;
private UUID m_notecardsFolder;
private UUID m_objectsFolder;
private void Init()
{
// Create an inventory that looks like this:
//
// /My Inventory
// <other system folders>
// /Objects
// Object 1
// Object 2
// Object 3
// /Notecards
// Notecard 1
// Notecard 2
// Notecard 3
// Notecard 4
// Notecard 5
m_scene = new SceneHelpers().SetupScene();
m_scene.InventoryService.CreateUserInventory(m_userID);
m_rootFolderID = m_scene.InventoryService.GetRootFolder(m_userID).ID;
InventoryFolderBase of = m_scene.InventoryService.GetFolderForType(m_userID, FolderType.Object);
m_objectsFolder = of.ID;
// Add 3 objects
InventoryItemBase item;
for (int i = 1; i <= 3; i++)
{
item = new InventoryItemBase(new UUID("b0000000-0000-0000-0000-0000000000b" + i), m_userID);
item.AssetID = UUID.Random();
item.AssetType = (int)AssetType.Object;
item.Folder = m_objectsFolder;
item.Name = "Object " + i;
m_scene.InventoryService.AddItem(item);
}
InventoryFolderBase ncf = m_scene.InventoryService.GetFolderForType(m_userID, FolderType.Notecard);
m_notecardsFolder = ncf.ID;
// Add 5 notecards
for (int i = 1; i <= 5; i++)
{
item = new InventoryItemBase(new UUID("10000000-0000-0000-0000-00000000000" + i), m_userID);
item.AssetID = UUID.Random();
item.AssetType = (int)AssetType.Notecard;
item.Folder = m_notecardsFolder;
item.Name = "Notecard " + i;
m_scene.InventoryService.AddItem(item);
}
}
[Test]
public void Test_001_RequestOne()
{
TestHelpers.InMethod();
Init();
FetchInventory2Handler handler = new FetchInventory2Handler(m_scene.InventoryService, m_userID);
TestOSHttpRequest req = new TestOSHttpRequest();
TestOSHttpResponse resp = new TestOSHttpResponse();
string request = "<llsd><map><key>items</key><array><map><key>item_id</key><uuid>";
request += "10000000-0000-0000-0000-000000000001"; // Notecard 1
request += "</uuid></map></array></map></llsd>";
string llsdresponse = handler.FetchInventoryRequest(request, "/FETCH", string.Empty, req, resp);
Assert.That(llsdresponse != null, Is.True, "Incorrect null response");
Assert.That(llsdresponse != string.Empty, Is.True, "Incorrect empty response");
Assert.That(llsdresponse.Contains(m_userID.ToString()), Is.True, "Response should contain userID");
Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000001"), Is.True, "Response does not contain item uuid");
Assert.That(llsdresponse.Contains("Notecard 1"), Is.True, "Response does not contain item Name");
Console.WriteLine(llsdresponse);
}
[Test]
public void Test_002_RequestMany()
{
TestHelpers.InMethod();
Init();
FetchInventory2Handler handler = new FetchInventory2Handler(m_scene.InventoryService, m_userID);
TestOSHttpRequest req = new TestOSHttpRequest();
TestOSHttpResponse resp = new TestOSHttpResponse();
string request = "<llsd><map><key>items</key><array>";
request += "<map><key>item_id</key><uuid>10000000-0000-0000-0000-000000000001</uuid></map>"; // Notecard 1
request += "<map><key>item_id</key><uuid>10000000-0000-0000-0000-000000000002</uuid></map>"; // Notecard 2
request += "<map><key>item_id</key><uuid>10000000-0000-0000-0000-000000000003</uuid></map>"; // Notecard 3
request += "<map><key>item_id</key><uuid>10000000-0000-0000-0000-000000000004</uuid></map>"; // Notecard 4
request += "<map><key>item_id</key><uuid>10000000-0000-0000-0000-000000000005</uuid></map>"; // Notecard 5
request += "</array></map></llsd>";
string llsdresponse = handler.FetchInventoryRequest(request, "/FETCH", string.Empty, req, resp);
Assert.That(llsdresponse != null, Is.True, "Incorrect null response");
Assert.That(llsdresponse != string.Empty, Is.True, "Incorrect empty response");
Assert.That(llsdresponse.Contains(m_userID.ToString()), Is.True, "Response should contain userID");
Console.WriteLine(llsdresponse);
Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000001"), Is.True, "Response does not contain notecard 1");
Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000002"), Is.True, "Response does not contain notecard 2");
Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000003"), Is.True, "Response does not contain notecard 3");
Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000004"), Is.True, "Response does not contain notecard 4");
Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000005"), Is.True, "Response does not contain notecard 5");
}
}
}

View File

@ -1,300 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
using log4net;
using log4net.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenSim.Capabilities.Handlers;
using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using OpenSim.Tests.Common;
namespace OpenSim.Capabilities.Handlers.FetchInventory.Tests
{
[TestFixture]
public class FetchInventoryDescendents2HandlerTests : OpenSimTestCase
{
private UUID m_userID = new UUID("00000000-0000-0000-0000-000000000001");
private Scene m_scene;
private UUID m_rootFolderID;
private int m_rootDescendents;
private UUID m_notecardsFolder;
private UUID m_objectsFolder;
private void Init()
{
// Create an inventory that looks like this:
//
// /My Inventory
// <other system folders>
// /Objects
// Some Object
// /Notecards
// Notecard 1
// Notecard 2
// /Test Folder
// Link to notecard -> /Notecards/Notecard 2
// Link to Objects folder -> /Objects
m_scene = new SceneHelpers().SetupScene();
m_scene.InventoryService.CreateUserInventory(m_userID);
m_rootFolderID = m_scene.InventoryService.GetRootFolder(m_userID).ID;
InventoryFolderBase of = m_scene.InventoryService.GetFolderForType(m_userID, FolderType.Object);
m_objectsFolder = of.ID;
// Add an object
InventoryItemBase item = new InventoryItemBase(new UUID("b0000000-0000-0000-0000-00000000000b"), m_userID);
item.AssetID = UUID.Random();
item.AssetType = (int)AssetType.Object;
item.Folder = m_objectsFolder;
item.Name = "Some Object";
m_scene.InventoryService.AddItem(item);
InventoryFolderBase ncf = m_scene.InventoryService.GetFolderForType(m_userID, FolderType.Notecard);
m_notecardsFolder = ncf.ID;
// Add a notecard
item = new InventoryItemBase(new UUID("10000000-0000-0000-0000-000000000001"), m_userID);
item.AssetID = UUID.Random();
item.AssetType = (int)AssetType.Notecard;
item.Folder = m_notecardsFolder;
item.Name = "Test Notecard 1";
m_scene.InventoryService.AddItem(item);
// Add another notecard
item.ID = new UUID("20000000-0000-0000-0000-000000000002");
item.AssetID = new UUID("a0000000-0000-0000-0000-00000000000a");
item.Name = "Test Notecard 2";
m_scene.InventoryService.AddItem(item);
// Add a folder
InventoryFolderBase folder = new InventoryFolderBase(new UUID("f0000000-0000-0000-0000-00000000000f"), "Test Folder", m_userID, m_rootFolderID);
folder.Type = (short)FolderType.None;
m_scene.InventoryService.AddFolder(folder);
// Add a link to notecard 2 in Test Folder
item.AssetID = item.ID; // use item ID of notecard 2
item.ID = new UUID("40000000-0000-0000-0000-000000000004");
item.AssetType = (int)AssetType.Link;
item.Folder = folder.ID;
item.Name = "Link to notecard";
m_scene.InventoryService.AddItem(item);
// Add a link to the Objects folder in Test Folder
item.AssetID = m_scene.InventoryService.GetFolderForType(m_userID, FolderType.Object).ID; // use item ID of Objects folder
item.ID = new UUID("50000000-0000-0000-0000-000000000005");
item.AssetType = (int)AssetType.LinkFolder;
item.Folder = folder.ID;
item.Name = "Link to Objects folder";
m_scene.InventoryService.AddItem(item);
InventoryCollection coll = m_scene.InventoryService.GetFolderContent(m_userID, m_rootFolderID);
m_rootDescendents = coll.Items.Count + coll.Folders.Count;
Console.WriteLine("Number of descendents: " + m_rootDescendents);
}
private string dorequest(FetchInvDescHandler handler, string request)
{
TestOSHttpRequest req = new TestOSHttpRequest();
TestOSHttpResponse resp = new TestOSHttpResponse();
using(ExpiringKey<UUID> bad = new ExpiringKey<UUID>(5000)) // bad but this is test
using (MemoryStream ms = new MemoryStream(Utils.StringToBytes(request), false))
{
req.InputStream = ms;
handler.FetchInventoryDescendentsRequest(req, resp, bad);
}
return Util.UTF8.GetString(resp.RawBuffer);
}
[Test]
public void Test_001_SimpleFolder()
{
TestHelpers.InMethod();
Init();
FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null, m_scene);
string request = "<llsd><map><key>folders</key><array><map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += m_rootFolderID;
request += "</uuid><key>owner_id</key><uuid>";
request += m_userID.ToString();
request += "</uuid><key>sort_order</key><integer>1</integer></map></array></map></llsd>";
string llsdresponse = dorequest(handler, request);
Assert.That(llsdresponse != null, Is.True, "Incorrect null response");
Assert.That(llsdresponse != string.Empty, Is.True, "Incorrect empty response");
Assert.That(llsdresponse.Contains(m_userID.ToString()), Is.True, "Response should contain userID");
string descendents = "descendents</key><integer>" + m_rootDescendents + "</integer>";
Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents");
Console.WriteLine(llsdresponse);
}
[Test]
public void Test_002_MultipleFolders()
{
TestHelpers.InMethod();
FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null, m_scene);
string request = "<llsd><map><key>folders</key><array>";
request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += m_rootFolderID;
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000001</uuid><key>sort_order</key><integer>1</integer></map>";
request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += m_notecardsFolder;
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000001</uuid><key>sort_order</key><integer>1</integer></map>";
request += "</array></map></llsd>";
string llsdresponse = dorequest(handler, request);
Console.WriteLine(llsdresponse);
string descendents = "descendents</key><integer>" + m_rootDescendents + "</integer>";
Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents for root folder");
descendents = "descendents</key><integer>2</integer>";
Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents for Notecard folder");
Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000001"), Is.True, "Notecard 1 is missing from response");
Assert.That(llsdresponse.Contains("20000000-0000-0000-0000-000000000002"), Is.True, "Notecard 2 is missing from response");
}
[Test]
public void Test_003_Links()
{
TestHelpers.InMethod();
FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null, m_scene);
string request = "<llsd><map><key>folders</key><array><map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += "f0000000-0000-0000-0000-00000000000f";
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000001</uuid><key>sort_order</key><integer>1</integer></map></array></map></llsd>";
string llsdresponse = dorequest(handler, request);
Console.WriteLine(llsdresponse);
string descendents = "descendents</key><integer>2</integer>";
Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents for Test Folder");
// Make sure that the note card link is included
Assert.That(llsdresponse.Contains("Link to notecard"), Is.True, "Link to notecard is missing");
//Make sure the notecard item itself is included
Assert.That(llsdresponse.Contains("Test Notecard 2"), Is.True, "Notecard 2 item (the source) is missing");
// Make sure that the source item is before the link item
int pos1 = llsdresponse.IndexOf("Test Notecard 2");
int pos2 = llsdresponse.IndexOf("Link to notecard");
Assert.Less(pos1, pos2, "Source of link is after link");
// Make sure the folder link is included
Assert.That(llsdresponse.Contains("Link to Objects folder"), Is.True, "Link to Objects folder is missing");
/* contents of link folder are not supposed to be listed
// Make sure the objects inside the Objects folder are included
// Note: I'm not entirely sure this is needed, but that's what I found in the implementation
Assert.That(llsdresponse.Contains("Some Object"), Is.True, "Some Object item (contents of the source) is missing");
*/
// Make sure that the source item is before the link item
pos1 = llsdresponse.IndexOf("Some Object");
pos2 = llsdresponse.IndexOf("Link to Objects folder");
Assert.Less(pos1, pos2, "Contents of source of folder link is after folder link");
}
[Test]
public void Test_004_DuplicateFolders()
{
TestHelpers.InMethod();
FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null, m_scene);
string request = "<llsd><map><key>folders</key><array>";
request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += m_rootFolderID;
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map>";
request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += m_notecardsFolder;
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map>";
request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += m_rootFolderID;
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map>";
request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += m_notecardsFolder;
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map>";
request += "</array></map></llsd>";
string llsdresponse = dorequest(handler, request);
Console.WriteLine(llsdresponse);
string root_folder = "<key>folder_id</key><uuid>" + m_rootFolderID + "</uuid>";
string notecards_folder = "<key>folder_id</key><uuid>" + m_notecardsFolder + "</uuid>";
Assert.That(llsdresponse.Contains(root_folder), "Missing root folder");
Assert.That(llsdresponse.Contains(notecards_folder), "Missing notecards folder");
int count = Regex.Matches(llsdresponse, root_folder).Count;
Assert.AreEqual(1, count, "More than 1 root folder in response");
count = Regex.Matches(llsdresponse, notecards_folder).Count;
Assert.AreEqual(2, count, "More than 1 notecards folder in response"); // Notecards will also be under root, so 2
}
[Test]
public void Test_005_FolderZero()
{
TestHelpers.InMethod();
Init();
FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null, m_scene);
string request = "<llsd><map><key>folders</key><array><map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
request += UUID.Zero;
request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map></array></map></llsd>";
string llsdresponse = dorequest(handler, request);
Assert.That(llsdresponse != null, Is.True, "Incorrect null response");
Assert.That(llsdresponse != string.Empty, Is.True, "Incorrect empty response");
// we do return a answer now
//Assert.That(llsdresponse.Contains("bad_folders</key><array><uuid>00000000-0000-0000-0000-000000000000"), Is.True, "Folder Zero should be a bad folder");
Console.WriteLine(llsdresponse);
}
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Capabilities;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Services.Interfaces;
using Caps = OpenSim.Framework.Capabilities.Caps;
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
using OSDMap = OpenMetaverse.StructuredData.OSDMap;
namespace OpenSim.Capabilities.Handlers
{
public class FetchInventory2Handler
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IInventoryService m_inventoryService;
public FetchInventory2Handler(IInventoryService invService)
{
m_inventoryService = invService;
}
public string FetchInventoryRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
// m_log.DebugFormat("[FETCH INVENTORY HANDLER]: Received FetchInventory capabilty request");
OSDMap requestmap = (OSDMap)OSDParser.DeserializeLLSDXml(Utils.StringToBytes(request));
OSDArray itemsRequested = (OSDArray)requestmap["items"];
string reply;
LLSDFetchInventory llsdReply = new LLSDFetchInventory();
foreach (OSDMap osdItemId in itemsRequested)
{
UUID itemId = osdItemId["item_id"].AsUUID();
InventoryItemBase item = m_inventoryService.GetItem(new InventoryItemBase(itemId));
if (item != null)
{
// We don't know the agent that this request belongs to so we'll use the agent id of the item
// which will be the same for all items.
llsdReply.agent_id = item.Owner;
llsdReply.items.Array.Add(ConvertInventoryItem(item));
}
}
reply = LLSDHelpers.SerialiseLLSDReply(llsdReply);
return reply;
}
/// <summary>
/// Convert an internal inventory item object into an LLSD object.
/// </summary>
/// <param name="invItem"></param>
/// <returns></returns>
private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem)
{
LLSDInventoryItem llsdItem = new LLSDInventoryItem();
llsdItem.asset_id = invItem.AssetID;
llsdItem.created_at = invItem.CreationDate;
llsdItem.desc = invItem.Description;
llsdItem.flags = (int)invItem.Flags;
llsdItem.item_id = invItem.ID;
llsdItem.name = invItem.Name;
llsdItem.parent_id = invItem.Folder;
llsdItem.type = invItem.AssetType;
llsdItem.inv_type = invItem.InvType;
llsdItem.permissions = new LLSDPermissions();
llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid;
llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions;
llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions;
llsdItem.permissions.group_id = invItem.GroupID;
llsdItem.permissions.group_mask = (int)invItem.GroupPermissions;
llsdItem.permissions.is_owner_group = invItem.GroupOwned;
llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions;
llsdItem.permissions.owner_id = invItem.Owner;
llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions;
llsdItem.sale_info = new LLSDSaleInfo();
llsdItem.sale_info.sale_price = invItem.SalePrice;
llsdItem.sale_info.sale_type = invItem.SaleType;
return llsdItem;
}
}
}

View File

@ -61,7 +61,7 @@ namespace OpenSim.Capabilities.Handlers
if (m_InventoryService == null)
throw new Exception(String.Format("Failed to load InventoryService from {0}; config is {1}", invService, m_ConfigName));
FetchInventory2Handler fiHandler = new FetchInventory2Handler(m_InventoryService, UUID.Zero);
FetchInventory2Handler fiHandler = new FetchInventory2Handler(m_InventoryService);
IRequestHandler reqHandler
= new RestStreamHandler(
"POST", "/CAPS/FetchInventory/", fiHandler.FetchInventoryRequest, "FetchInventory", null);

View File

@ -1,190 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Services.Interfaces;
using Caps = OpenSim.Framework.Capabilities.Caps;
namespace OpenSim.Capabilities.Handlers
{
public class GetAssetsHandler
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly Dictionary<string, AssetType> queryTypes = new Dictionary<string, AssetType>()
{
{"texture_id", AssetType.Texture},
{"sound_id", AssetType.Sound},
{"callcard_id", AssetType.CallingCard},
{"landmark_id", AssetType.Landmark},
{"script_id", AssetType.LSLText},
{"clothing_id", AssetType.Clothing},
{"object_id", AssetType.Object},
{"notecard_id", AssetType.Notecard},
{"lsltext_id", AssetType.LSLText},
{"lslbyte_id", AssetType.LSLBytecode},
{"txtr_tga_id", AssetType.TextureTGA},
{"bodypart_id", AssetType.Bodypart},
{"snd_wav_id", AssetType.SoundWAV},
{"img_tga_id", AssetType.ImageTGA},
{"jpeg_id", AssetType.ImageJPEG},
{"animatn_id", AssetType.Animation},
{"gesture_id", AssetType.Gesture},
{"mesh_id", AssetType.Mesh},
{"settings_id", AssetType.Settings}
};
private IAssetService m_assetService;
public GetAssetsHandler(IAssetService assService)
{
m_assetService = assService;
}
public void Handle(OSHttpRequest req, OSHttpResponse response)
{
response.ContentType = "text/plain";
if (m_assetService == null)
{
response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
response.KeepAlive = false;
return;
}
response.StatusCode = (int)HttpStatusCode.BadRequest;
var queries = req.QueryAsDictionary;
if(queries.Count == 0)
return;
AssetType type = AssetType.Unknown;
string assetStr = string.Empty;
foreach (KeyValuePair<string,string> kvp in queries)
{
if (queryTypes.ContainsKey(kvp.Key))
{
type = queryTypes[kvp.Key];
assetStr = kvp.Value;
break;
}
}
if(type == AssetType.Unknown)
{
//m_log.Warn("[GETASSET]: Unknown type: " + query);
m_log.Warn("[GETASSET]: Unknown type");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if (String.IsNullOrEmpty(assetStr))
return;
UUID assetID = UUID.Zero;
if(!UUID.TryParse(assetStr, out assetID))
return;
AssetBase asset = m_assetService.Get(assetID.ToString());
if(asset == null)
{
// m_log.Warn("[GETASSET]: not found: " + query + " " + assetStr);
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if (asset.Type != (sbyte)type)
return;
int len = asset.Data.Length;
string range = null;
if (req.Headers["Range"] != null)
range = req.Headers["Range"];
else if (req.Headers["range"] != null)
range = req.Headers["range"];
// range request
int start, end;
if (Util.TryParseHttpRange(range, out start, out end))
{
// Before clamping start make sure we can satisfy it in order to avoid
// sending back the last byte instead of an error status
if (start >= asset.Data.Length)
{
response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
return;
}
if (end == -1)
end = asset.Data.Length - 1;
else
end = Utils.Clamp(end, 0, asset.Data.Length - 1);
start = Utils.Clamp(start, 0, end);
len = end - start + 1;
//m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, asset.Data.Length));
response.StatusCode = (int)HttpStatusCode.PartialContent;
response.RawBufferStart = start;
}
else
response.StatusCode = (int)HttpStatusCode.OK;
response.ContentType = asset.Metadata.ContentType;
response.RawBuffer = asset.Data;
response.RawBufferLen = len;
if (type == AssetType.Mesh || type == AssetType.Texture)
{
if(len > 8196)
{
//if(type == AssetType.Texture && ((asset.Flags & AssetFlags.AvatarBake)!= 0))
// responsedata["prio"] = 1;
//else
response.Priority = 2;
}
else
response.Priority = 1;
}
else
response.Priority = -1;
}
}
}

View File

@ -43,112 +43,73 @@ using Caps = OpenSim.Framework.Capabilities.Caps;
namespace OpenSim.Capabilities.Handlers
{
public class GetMeshHandler
public class GetMeshHandler
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// private static readonly ILog m_log =
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IAssetService m_assetService;
public const string DefaultFormat = "vnd.ll.mesh";
public GetMeshHandler(IAssetService assService)
{
m_assetService = assService;
}
public Hashtable Handle(Hashtable request)
{
return ProcessGetMesh(request, UUID.Zero, null); ;
}
public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap)
{
Hashtable responsedata = new Hashtable();
if (m_assetService == null)
{
responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.ServiceUnavailable;
responsedata["str_response_string"] = "The asset service is unavailable";
responsedata["keepalive"] = false;
return responsedata;
}
responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.BadRequest;
responsedata["int_response_code"] = 400; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["int_bytes"] = 0;
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Request wasn't what was expected";
string meshStr = string.Empty;
if (request.ContainsKey("mesh_id"))
meshStr = request["mesh_id"].ToString();
if (String.IsNullOrEmpty(meshStr))
return responsedata;
UUID meshID = UUID.Zero;
if(!UUID.TryParse(meshStr, out meshID))
return responsedata;
AssetBase mesh = m_assetService.Get(meshID.ToString());
if(mesh == null)
if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
{
responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
responsedata["str_response_string"] = "Mesh not found.";
return responsedata;
}
if (mesh.Type != (SByte)AssetType.Mesh)
{
responsedata["str_response_string"] = "Asset isn't a mesh.";
return responsedata;
}
string range = String.Empty;
if (((Hashtable)request["headers"])["range"] != null)
range = (string)((Hashtable)request["headers"])["range"];
else if (((Hashtable)request["headers"])["Range"] != null)
range = (string)((Hashtable)request["headers"])["Range"];
responsedata["content_type"] = "application/vnd.ll.mesh";
if (String.IsNullOrEmpty(range))
{
// full mesh
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.OK;
return responsedata;
}
// range request
int start, end;
if (Util.TryParseHttpRange(range, out start, out end))
{
// Before clamping start make sure we can satisfy it in order to avoid
// sending back the last byte instead of an error status
if (start >= mesh.Data.Length)
if (m_assetService == null)
{
responsedata["str_response_string"] = "This range doesnt exist.";
responsedata["int_response_code"] = 404; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
return responsedata;
}
end = Utils.Clamp(end, 0, mesh.Data.Length - 1);
start = Utils.Clamp(start, 0, end);
int len = end - start + 1;
AssetBase mesh = m_assetService.Get(meshID.ToString());
//m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
Hashtable headers = new Hashtable();
headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end, mesh.Data.Length);
responsedata["headers"] = headers;
responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.PartialContent;
byte[] d = new byte[len];
Array.Copy(mesh.Data, start, d, 0, len);
responsedata["bin_response_data"] = d;
responsedata["int_bytes"] = len;
return responsedata;
if (mesh != null)
{
if (mesh.Type == (SByte)AssetType.Mesh)
{
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
responsedata["content_type"] = "application/vnd.ll.mesh";
responsedata["int_response_code"] = 200;
}
// Optionally add additional mesh types here
else
{
responsedata["int_response_code"] = 404; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh.";
return responsedata;
}
}
else
{
responsedata["int_response_code"] = 404; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!";
return responsedata;
}
}
m_log.Warn("[GETMESH]: Failed to parse a range from GetMesh request, sending full asset: " + (string)request["uri"]);
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
responsedata["int_response_code"] = (int)System.Net.HttpStatusCode.OK;
return responsedata;
}
}

View File

@ -25,13 +25,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Server.Base;
using OpenSim.Server.Handlers.Base;
using OpenSim.Services.Interfaces;
using System;
using System.Collections;
using Nini.Config;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Server.Handlers.Base;
using OpenSim.Framework.Servers;
using OpenMetaverse;
namespace OpenSim.Capabilities.Handlers
{
@ -62,17 +65,15 @@ namespace OpenSim.Capabilities.Handlers
if (m_AssetService == null)
throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
string rurl = serverConfig.GetString("GetMeshRedirectURL");
GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
IRequestHandler reqHandler
= new RestHTTPHandler(
"GET",
"/" + UUID.Random(),
"/CAPS/" + UUID.Random(),
httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
"GetMesh",
null);
server.AddStreamHandler(reqHandler); ;
server.AddStreamHandler(reqHandler);
}
}
}

View File

@ -47,92 +47,85 @@ using Caps = OpenSim.Framework.Capabilities.Caps;
namespace OpenSim.Capabilities.Handlers
{
public class GetTextureHandler
public class GetTextureHandler : BaseStreamHandler
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IAssetService m_assetService;
public const string DefaultFormat = "x-j2c";
public GetTextureHandler(IAssetService assService)
// TODO: Change this to a config option
const string REDIRECT_URL = null;
public GetTextureHandler(string path, IAssetService assService, string name, string description)
: base("GET", path, name, description)
{
m_assetService = assService;
}
public Hashtable Handle(Hashtable request)
public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
Hashtable ret = new Hashtable();
ret["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
ret["content_type"] = "text/plain";
ret["int_bytes"] = 0;
string textureStr = (string)request["texture_id"];
string format = (string)request["format"];
// Try to parse the texture ID from the request URL
NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
string textureStr = query.GetOne("texture_id");
string format = query.GetOne("format");
//m_log.DebugFormat("[GETTEXTURE]: called {0}", textureStr);
if (m_assetService == null)
{
m_log.Error("[GETTEXTURE]: Cannot fetch texture " + textureStr + " without an asset service");
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
}
UUID textureID;
if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out textureID))
{
// m_log.DebugFormat("[GETTEXTURE]: Received request for texture id {0}", textureID);
string[] formats;
if (!string.IsNullOrEmpty(format))
if (format != null && format != string.Empty)
{
formats = new string[1] { format.ToLower() };
}
else
{
formats = new string[1] { DefaultFormat }; // default
if (((Hashtable)request["headers"])["Accept"] != null)
formats = WebUtil.GetPreferredImageTypes((string)((Hashtable)request["headers"])["Accept"]);
formats = WebUtil.GetPreferredImageTypes(httpRequest.Headers.Get("Accept"));
if (formats.Length == 0)
formats = new string[1] { DefaultFormat }; // default
}
// OK, we have an array with preferred formats, possibly with only one entry
bool foundtexture = false;
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
foreach (string f in formats)
{
foundtexture = FetchTexture(request, ret, textureID, f);
if (foundtexture)
if (FetchTexture(httpRequest, httpResponse, textureID, f))
break;
}
if (!foundtexture)
{
ret["int_response_code"] = 404;
ret["error_status_text"] = "not found";
ret["str_response_string"] = "not found";
ret["content_type"] = "text/plain";
ret["int_bytes"] = 0;
}
}
else
{
m_log.Warn("[GETTEXTURE]: Failed to parse a texture_id from GetTexture request: " + (string)request["uri"]);
m_log.Warn("[GETTEXTURE]: Failed to parse a texture_id from GetTexture request: " + httpRequest.Url);
}
// m_log.DebugFormat(
// "[GETTEXTURE]: For texture {0} sending back response {1}, data length {2}",
// textureID, httpResponse.StatusCode, httpResponse.ContentLength);
return ret;
return null;
}
/// <summary>
///
///
/// </summary>
/// <param name="httpRequest"></param>
/// <param name="httpResponse"></param>
/// <param name="textureID"></param>
/// <param name="format"></param>
/// <returns>False for "caller try another codec"; true otherwise</returns>
private bool FetchTexture(Hashtable request, Hashtable response, UUID textureID, string format)
private bool FetchTexture(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse, UUID textureID, string format)
{
// m_log.DebugFormat("[GETTEXTURE]: {0} with requested format {1}", textureID, format);
AssetBase texture;
@ -141,84 +134,98 @@ namespace OpenSim.Capabilities.Handlers
if (format != DefaultFormat)
fullID = fullID + "-" + format;
// try the cache
texture = m_assetService.GetCached(fullID);
if (texture == null)
if (!String.IsNullOrEmpty(REDIRECT_URL))
{
//m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
// Fetch locally or remotely. Misses return a 404
texture = m_assetService.Get(textureID.ToString());
// Only try to fetch locally cached textures. Misses are redirected
texture = m_assetService.GetCached(fullID);
if (texture != null)
{
if (texture.Type != (sbyte)AssetType.Texture)
return true;
if (format == DefaultFormat)
{
WriteTextureData(request, response, texture, format);
return true;
}
else
{
AssetBase newTexture = new AssetBase(texture.ID + "-" + format, texture.Name, (sbyte)AssetType.Texture, texture.Metadata.CreatorID);
newTexture.Data = ConvertTextureData(texture, format);
if (newTexture.Data.Length == 0)
return false; // !!! Caller try another codec, please!
newTexture.Flags = AssetFlags.Collectable;
newTexture.Temporary = true;
newTexture.Local = true;
m_assetService.Store(newTexture);
WriteTextureData(request, response, newTexture, format);
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
return true;
}
WriteTextureData(httpRequest, httpResponse, texture, format);
}
}
else // it was on the cache
{
//m_log.DebugFormat("[GETTEXTURE]: texture was in the cache");
WriteTextureData(request, response, texture, format);
return true;
}
else
{
string textureUrl = REDIRECT_URL + textureID.ToString();
m_log.Debug("[GETTEXTURE]: Redirecting texture request to " + textureUrl);
httpResponse.RedirectLocation = textureUrl;
return true;
}
}
else // no redirect
{
// try the cache
texture = m_assetService.GetCached(fullID);
//response = new Hashtable();
if (texture == null)
{
// m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
// Fetch locally or remotely. Misses return a 404
texture = m_assetService.Get(textureID.ToString());
if (texture != null)
{
if (texture.Type != (sbyte)AssetType.Texture)
{
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
return true;
}
if (format == DefaultFormat)
{
WriteTextureData(httpRequest, httpResponse, texture, format);
return true;
}
else
{
AssetBase newTexture = new AssetBase(texture.ID + "-" + format, texture.Name, (sbyte)AssetType.Texture, texture.Metadata.CreatorID);
newTexture.Data = ConvertTextureData(texture, format);
if (newTexture.Data.Length == 0)
return false; // !!! Caller try another codec, please!
newTexture.Flags = AssetFlags.Collectable;
newTexture.Temporary = true;
m_assetService.Store(newTexture);
WriteTextureData(httpRequest, httpResponse, newTexture, format);
return true;
}
}
}
else // it was on the cache
{
// m_log.DebugFormat("[GETTEXTURE]: texture was in the cache");
WriteTextureData(httpRequest, httpResponse, texture, format);
return true;
}
}
//WriteTextureData(request,response,null,format);
// not found
//m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found");
return false;
// m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found");
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
return true;
}
private void WriteTextureData(Hashtable request, Hashtable response, AssetBase texture, string format)
private void WriteTextureData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture, string format)
{
Hashtable headers = new Hashtable();
response["headers"] = headers;
string range = String.Empty;
if (((Hashtable)request["headers"])["range"] != null)
range = (string)((Hashtable)request["headers"])["range"];
else if (((Hashtable)request["headers"])["Range"] != null)
range = (string)((Hashtable)request["headers"])["Range"];
string range = request.Headers.GetOne("Range");
if (!String.IsNullOrEmpty(range)) // JP2's only
{
// Range request
int start, end;
if (Util.TryParseHttpRange(range, out start, out end))
if (TryParseRange(range, out start, out end))
{
// Before clamping start make sure we can satisfy it in order to avoid
// sending back the last byte instead of an error status
if (start >= texture.Data.Length)
{
// m_log.DebugFormat(
// "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
// texture.ID, start, texture.Data.Length);
m_log.DebugFormat(
"[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
texture.ID, start, texture.Data.Length);
// Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back
// Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations
@ -232,51 +239,54 @@ namespace OpenSim.Capabilities.Handlers
// However, if we return PartialContent (or OK) instead, the viewer will display that resolution.
// response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
// viewers don't seem to handle RequestedRangeNotSatisfiable and keep retrying with same parameters
response["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
// response.AddHeader("Content-Range", String.Format("bytes */{0}", texture.Data.Length));
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
response.ContentType = texture.Metadata.ContentType;
}
else
{
// Handle the case where no second range value was given. This is equivalent to requesting
// the rest of the entity.
if (end == -1)
end = int.MaxValue;
end = Utils.Clamp(end, 0, texture.Data.Length - 1);
start = Utils.Clamp(start, 0, end);
int len = end - start + 1;
// m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
response["content-type"] = texture.Metadata.ContentType;
response["int_response_code"] = (int)System.Net.HttpStatusCode.PartialContent;
headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length);
// Always return PartialContent, even if the range covered the entire data length
// We were accidentally sending back 404 before in this situation
// https://issues.apache.org/bugzilla/show_bug.cgi?id=51878 supports sending 206 even if the
// entire range is requested, and viewer 3.2.2 (and very probably earlier) seems fine with this.
//
// We also do not want to send back OK even if the whole range was satisfiable since this causes
// HTTP textures on at least Imprudence 1.4.0-beta2 to never display the final texture quality.
// if (end > maxEnd)
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
// else
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
byte[] d = new byte[len];
Array.Copy(texture.Data, start, d, 0, len);
response["bin_response_data"] = d;
response["int_bytes"] = len;
response.ContentLength = len;
response.ContentType = texture.Metadata.ContentType;
response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length));
response.Body.Write(texture.Data, start, len);
}
}
else
{
m_log.Warn("[GETTEXTURE]: Malformed Range header: " + range);
response["int_response_code"] = (int)System.Net.HttpStatusCode.BadRequest;
response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
}
}
else // JP2's or other formats
{
// Full content request
response["int_response_code"] = (int)System.Net.HttpStatusCode.OK;
response.StatusCode = (int)System.Net.HttpStatusCode.OK;
response.ContentLength = texture.Data.Length;
if (format == DefaultFormat)
response["content_type"] = texture.Metadata.ContentType;
response.ContentType = texture.Metadata.ContentType;
else
response["content_type"] = "image/" + format;
response["bin_response_data"] = texture.Data;
response["int_bytes"] = texture.Data.Length;
// response.Body.Write(texture.Data, 0, texture.Data.Length);
response.ContentType = "image/" + format;
response.Body.Write(texture.Data, 0, texture.Data.Length);
}
// if (response.StatusCode < 200 || response.StatusCode > 299)
@ -289,41 +299,58 @@ namespace OpenSim.Capabilities.Handlers
// texture.FullID, range, response.StatusCode, response.ContentLength, texture.Data.Length);
}
private bool TryParseRange(string header, out int start, out int end)
{
if (header.StartsWith("bytes="))
{
string[] rangeValues = header.Substring(6).Split('-');
if (rangeValues.Length == 2)
{
if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end))
return true;
}
}
start = end = 0;
return false;
}
private byte[] ConvertTextureData(AssetBase texture, string format)
{
m_log.DebugFormat("[GETTEXTURE]: Converting texture {0} to {1}", texture.ID, format);
byte[] data = new byte[0];
MemoryStream imgstream = new MemoryStream();
Bitmap mTexture = null;
ManagedImage managedImage = null;
Image image = null;
Bitmap mTexture = new Bitmap(1, 1);
ManagedImage managedImage;
Image image = (Image)mTexture;
try
{
// Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular data
imgstream = new MemoryStream();
// Decode image to System.Drawing.Image
if (OpenJPEG.DecodeToImage(texture.Data, out managedImage, out image) && image != null)
if (OpenJPEG.DecodeToImage(texture.Data, out managedImage, out image))
{
// Save to bitmap
mTexture = new Bitmap(image);
using(EncoderParameters myEncoderParameters = new EncoderParameters())
{
myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality,95L);
EncoderParameters myEncoderParameters = new EncoderParameters();
myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L);
// Save bitmap to stream
ImageCodecInfo codec = GetEncoderInfo("image/" + format);
if (codec != null)
{
mTexture.Save(imgstream, codec, myEncoderParameters);
// Save bitmap to stream
ImageCodecInfo codec = GetEncoderInfo("image/" + format);
if (codec != null)
{
mTexture.Save(imgstream, codec, myEncoderParameters);
// Write the stream to a byte array for output
data = imgstream.ToArray();
}
else
m_log.WarnFormat("[GETTEXTURE]: No such codec {0}", format);
data = imgstream.ToArray();
}
else
m_log.WarnFormat("[GETTEXTURE]: No such codec {0}", format);
}
}
catch (Exception e)
@ -340,10 +367,11 @@ namespace OpenSim.Capabilities.Handlers
if (image != null)
image.Dispose();
if(managedImage != null)
managedImage.Clear();
if (imgstream != null)
{
imgstream.Close();
imgstream.Dispose();
}
}
return data;
@ -362,4 +390,4 @@ namespace OpenSim.Capabilities.Handlers
return null;
}
}
}
}

View File

@ -1,394 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Drawing;
using System.Drawing.Imaging;
using System.Reflection;
using System.IO;
using System.Net;
using System.Web;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Imaging;
using OpenSim.Framework;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
using Caps = OpenSim.Framework.Capabilities.Caps;
namespace OpenSim.Capabilities.Handlers
{
public class GetTextureRobustHandler : BaseStreamHandler
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IAssetService m_assetService;
public const string DefaultFormat = "x-j2c";
// TODO: Change this to a config option
private string m_RedirectURL = null;
public GetTextureRobustHandler(string path, IAssetService assService, string name, string description, string redirectURL)
: base("GET", path, name, description)
{
m_assetService = assService;
m_RedirectURL = redirectURL;
if (m_RedirectURL != null && !m_RedirectURL.EndsWith("/"))
m_RedirectURL += "/";
}
protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
// Try to parse the texture ID from the request URL
NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
string textureStr = query.GetOne("texture_id");
string format = query.GetOne("format");
//m_log.DebugFormat("[GETTEXTURE]: called {0}", textureStr);
if (m_assetService == null)
{
m_log.Error("[GETTEXTURE]: Cannot fetch texture " + textureStr + " without an asset service");
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
return null;
}
UUID textureID;
if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out textureID))
{
// m_log.DebugFormat("[GETTEXTURE]: Received request for texture id {0}", textureID);
string[] formats;
if (!string.IsNullOrEmpty(format))
{
formats = new string[1] { format.ToLower() };
}
else
{
formats = WebUtil.GetPreferredImageTypes(httpRequest.Headers.Get("Accept"));
if (formats.Length == 0)
formats = new string[1] { DefaultFormat }; // default
}
// OK, we have an array with preferred formats, possibly with only one entry
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
foreach (string f in formats)
{
if (FetchTexture(httpRequest, httpResponse, textureID, f))
break;
}
}
else
{
m_log.Warn("[GETTEXTURE]: Failed to parse a texture_id from GetTexture request: " + httpRequest.Url);
}
// m_log.DebugFormat(
// "[GETTEXTURE]: For texture {0} sending back response {1}, data length {2}",
// textureID, httpResponse.StatusCode, httpResponse.ContentLength);
return null;
}
/// <summary>
///
/// </summary>
/// <param name="httpRequest"></param>
/// <param name="httpResponse"></param>
/// <param name="textureID"></param>
/// <param name="format"></param>
/// <returns>False for "caller try another codec"; true otherwise</returns>
private bool FetchTexture(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse, UUID textureID, string format)
{
// m_log.DebugFormat("[GETTEXTURE]: {0} with requested format {1}", textureID, format);
if(!String.IsNullOrEmpty(m_RedirectURL))
{
string textureUrl = m_RedirectURL + "?texture_id=" + textureID.ToString();
m_log.Debug("[GETTEXTURE]: Redirecting texture request to " + textureUrl);
httpResponse.StatusCode = (int)HttpStatusCode.Moved;
httpResponse.AddHeader("Location:", textureUrl);
return true;
}
// Fetch, Misses or invalid return a 404
AssetBase texture = m_assetService.Get(textureID.ToString());
if (texture != null)
{
if (texture.Type != (sbyte)AssetType.Texture)
{
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
return true;
}
if (format == DefaultFormat)
{
WriteTextureData(httpRequest, httpResponse, texture, format);
return true;
}
// need to convert format
AssetBase newTexture = new AssetBase(texture.ID + "-" + format, texture.Name, (sbyte)AssetType.Texture, texture.Metadata.CreatorID);
newTexture.Data = ConvertTextureData(texture, format);
if (newTexture.Data.Length == 0)
return false; // !!! Caller try another codec, please!
newTexture.Flags = AssetFlags.Collectable;
newTexture.Temporary = true;
newTexture.Local = true;
WriteTextureData(httpRequest, httpResponse, newTexture, format);
return true;
}
// not found
// m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found");
httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
return true;
}
private void WriteTextureData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture, string format)
{
string range = request.Headers.GetOne("Range");
if (!String.IsNullOrEmpty(range)) // JP2's only
{
// Range request
int start, end;
if (TryParseRange(range, out start, out end))
{
// Before clamping start make sure we can satisfy it in order to avoid
// sending back the last byte instead of an error status
if (start >= texture.Data.Length)
{
// m_log.DebugFormat(
// "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
// texture.ID, start, texture.Data.Length);
// Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back
// Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations
// of the Linden Lab viewer (3.2.1 and 3.3.4 and probably earlier), a viewer that has previously
// received a very small texture may attempt to fetch bytes from the server past the
// range of data that it received originally. Whether this happens appears to depend on whether
// the viewer's estimation of how large a request it needs to make for certain discard levels
// (http://wiki.secondlife.com/wiki/Image_System#Discard_Level_and_Mip_Mapping), chiefly discard
// level 2. If this estimate is greater than the total texture size, returning a RequestedRangeNotSatisfiable
// here will cause the viewer to treat the texture as bad and never display the full resolution
// However, if we return PartialContent (or OK) instead, the viewer will display that resolution.
// response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
// response.AddHeader("Content-Range", String.Format("bytes */{0}", texture.Data.Length));
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
response.ContentType = texture.Metadata.ContentType;
}
else
{
// Handle the case where no second range value was given. This is equivalent to requesting
// the rest of the entity.
if (end == -1)
end = int.MaxValue;
end = Utils.Clamp(end, 0, texture.Data.Length - 1);
start = Utils.Clamp(start, 0, end);
int len = end - start + 1;
// m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
// Always return PartialContent, even if the range covered the entire data length
// We were accidentally sending back 404 before in this situation
// https://issues.apache.org/bugzilla/show_bug.cgi?id=51878 supports sending 206 even if the
// entire range is requested, and viewer 3.2.2 (and very probably earlier) seems fine with this.
//
// We also do not want to send back OK even if the whole range was satisfiable since this causes
// HTTP textures on at least Imprudence 1.4.0-beta2 to never display the final texture quality.
// if (end > maxEnd)
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
// else
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
response.ContentLength = len;
response.ContentType = texture.Metadata.ContentType;
response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length));
response.RawBuffer = texture.Data;
response.RawBufferStart = start;
response.RawBufferLen = len;
}
}
else
{
m_log.Warn("[GETTEXTURE]: Malformed Range header: " + range);
response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
}
}
else // JP2's or other formats
{
// Full content request
response.StatusCode = (int)System.Net.HttpStatusCode.OK;
response.ContentLength = texture.Data.Length;
if (format == DefaultFormat)
response.ContentType = texture.Metadata.ContentType;
else
response.ContentType = "image/" + format;
response.RawBuffer = texture.Data;
response.RawBufferStart = 0;
response.RawBufferLen = texture.Data.Length;
}
// if (response.StatusCode < 200 || response.StatusCode > 299)
// m_log.WarnFormat(
// "[GETTEXTURE]: For texture {0} requested range {1} responded {2} with content length {3} (actual {4})",
// texture.FullID, range, response.StatusCode, response.ContentLength, texture.Data.Length);
// else
// m_log.DebugFormat(
// "[GETTEXTURE]: For texture {0} requested range {1} responded {2} with content length {3} (actual {4})",
// texture.FullID, range, response.StatusCode, response.ContentLength, texture.Data.Length);
}
/// <summary>
/// Parse a range header.
/// </summary>
/// <remarks>
/// As per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html,
/// this obeys range headers with two values (e.g. 533-4165) and no second value (e.g. 533-).
/// Where there is no value, -1 is returned.
/// FIXME: Need to cover the case where only a second value is specified (e.g. -4165), probably by returning -1
/// for start.</remarks>
/// <returns></returns>
/// <param name='header'></param>
/// <param name='start'>Start of the range. Undefined if this was not a number.</param>
/// <param name='end'>End of the range. Will be -1 if no end specified. Undefined if there was a raw string but this was not a number.</param>
private bool TryParseRange(string header, out int start, out int end)
{
start = end = 0;
if (header.StartsWith("bytes="))
{
string[] rangeValues = header.Substring(6).Split('-');
if (rangeValues.Length == 2)
{
if (!Int32.TryParse(rangeValues[0], out start))
return false;
string rawEnd = rangeValues[1];
if (rawEnd == "")
{
end = -1;
return true;
}
else if (Int32.TryParse(rawEnd, out end))
{
return true;
}
}
}
start = end = 0;
return false;
}
private byte[] ConvertTextureData(AssetBase texture, string format)
{
m_log.DebugFormat("[GETTEXTURE]: Converting texture {0} to {1}", texture.ID, format);
byte[] data = new byte[0];
MemoryStream imgstream = new MemoryStream();
Bitmap mTexture = null;
ManagedImage managedImage = null;
Image image = null;
try
{
// Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular data
// Decode image to System.Drawing.Image
if (OpenJPEG.DecodeToImage(texture.Data, out managedImage, out image) && image != null)
{
// Save to bitmap
mTexture = new Bitmap(image);
using(EncoderParameters myEncoderParameters = new EncoderParameters())
{
myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality,95L);
// Save bitmap to stream
ImageCodecInfo codec = GetEncoderInfo("image/" + format);
if (codec != null)
{
mTexture.Save(imgstream, codec, myEncoderParameters);
// Write the stream to a byte array for output
data = imgstream.ToArray();
}
else
m_log.WarnFormat("[GETTEXTURE]: No such codec {0}", format);
}
}
}
catch (Exception e)
{
m_log.WarnFormat("[GETTEXTURE]: Unable to convert texture {0} to {1}: {2}", texture.ID, format, e.Message);
}
finally
{
// Reclaim memory, these are unmanaged resources
// If we encountered an exception, one or more of these will be null
if (mTexture != null)
mTexture.Dispose();
if (image != null)
image.Dispose();
if(managedImage != null)
managedImage.Clear();
if (imgstream != null)
imgstream.Dispose();
}
return data;
}
// From msdn
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (int j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
}
}

View File

@ -33,7 +33,6 @@ using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Server.Handlers.Base;
using OpenMetaverse;
namespace OpenSim.Capabilities.Handlers
{
public class GetTextureServerConnector : ServiceConnector
@ -63,11 +62,8 @@ namespace OpenSim.Capabilities.Handlers
if (m_AssetService == null)
throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
string rurl = serverConfig.GetString("GetTextureRedirectURL");
;
server.AddStreamHandler(
new GetTextureRobustHandler("/CAPS/GetTexture", m_AssetService, "GetTexture", null, rurl));
new GetTextureHandler("/CAPS/GetTexture/" /*+ UUID.Random() */, m_AssetService, "GetTexture", null));
}
}
}
}

View File

@ -37,12 +37,12 @@ using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Tests.Common;
using OpenSim.Tests.Common.Mock;
/*
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
{
[TestFixture]
public class GetTextureHandlerTests : OpenSimTestCase
public class GetTextureHandlerTests
{
[Test]
public void TestTextureNotFound()
@ -52,7 +52,7 @@ namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
// Overkill - we only really need the asset service, not a whole scene.
Scene scene = new SceneHelpers().SetupScene();
GetTextureHandler handler = new GetTextureHandler("/gettexture", scene.AssetService, "TestGetTexture", null, null);
GetTextureHandler handler = new GetTextureHandler(null, scene.AssetService, "TestGetTexture", null);
TestOSHttpRequest req = new TestOSHttpRequest();
TestOSHttpResponse resp = new TestOSHttpResponse();
req.Url = new Uri("http://localhost/?texture_id=00000000-0000-1111-9999-000000000012");
@ -60,5 +60,4 @@ namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
Assert.That(resp.StatusCode, Is.EqualTo((int)System.Net.HttpStatusCode.NotFound));
}
}
}
*/
}

View File

@ -1,33 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenSim.Capabilities.Handlers")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("http://opensimulator.org")]
[assembly: AssemblyProduct("OpenSim")]
[assembly: AssemblyCopyright("OpenSimulator developers")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("32350823-e1df-45e3-b7fa-0a58b4372433")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]

View File

@ -0,0 +1,181 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Drawing;
using System.Drawing.Imaging;
using System.Reflection;
using System.IO;
using System.Web;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Imaging;
using OpenSim.Framework;
using OpenSim.Framework.Capabilities;
using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
using Caps = OpenSim.Framework.Capabilities.Caps;
namespace OpenSim.Capabilities.Handlers
{
public class UploadBakedTextureHandler
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Caps m_HostCapsObj;
private IAssetService m_assetService;
private bool m_persistBakedTextures;
public UploadBakedTextureHandler(Caps caps, IAssetService assetService, bool persistBakedTextures)
{
m_HostCapsObj = caps;
m_assetService = assetService;
m_persistBakedTextures = persistBakedTextures;
}
/// <summary>
/// Handle a request from the client for a Uri to upload a baked texture.
/// </summary>
/// <param name="request"></param>
/// <param name="path"></param>
/// <param name="param"></param>
/// <param name="httpRequest"></param>
/// <param name="httpResponse"></param>
/// <returns>The upload response if the request is successful, null otherwise.</returns>
public string UploadBakedTexture(
string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
try
{
string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath;
string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000");
BakedTextureUploader uploader =
new BakedTextureUploader(capsBase + uploaderPath, m_HostCapsObj.HttpListener);
uploader.OnUpLoad += BakedTextureUploaded;
m_HostCapsObj.HttpListener.AddStreamHandler(
new BinaryStreamHandler(
"POST", capsBase + uploaderPath, uploader.uploaderCaps, "UploadBakedTexture", null));
string protocol = "http://";
if (m_HostCapsObj.SSLCaps)
protocol = "https://";
string uploaderURL = protocol + m_HostCapsObj.HostName + ":" +
m_HostCapsObj.Port.ToString() + capsBase + uploaderPath;
LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse();
uploadResponse.uploader = uploaderURL;
uploadResponse.state = "upload";
return LLSDHelpers.SerialiseLLSDReply(uploadResponse);
}
catch (Exception e)
{
m_log.ErrorFormat("[UPLOAD BAKED TEXTURE HANDLER]: {0}{1}", e.Message, e.StackTrace);
}
return null;
}
/// <summary>
/// Called when a baked texture has been successfully uploaded by a client.
/// </summary>
/// <param name="assetID"></param>
/// <param name="data"></param>
private void BakedTextureUploaded(UUID assetID, byte[] data)
{
// m_log.DebugFormat("[UPLOAD BAKED TEXTURE HANDLER]: Received baked texture {0}", assetID.ToString());
AssetBase asset;
asset = new AssetBase(assetID, "Baked Texture", (sbyte)AssetType.Texture, m_HostCapsObj.AgentID.ToString());
asset.Data = data;
asset.Temporary = true;
asset.Local = !m_persistBakedTextures; // Local assets aren't persisted, non-local are
m_assetService.Store(asset);
}
}
class BakedTextureUploader
{
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public event Action<UUID, byte[]> OnUpLoad;
private string uploaderPath = String.Empty;
private UUID newAssetID;
private IHttpServer httpListener;
public BakedTextureUploader(string path, IHttpServer httpServer)
{
newAssetID = UUID.Random();
uploaderPath = path;
httpListener = httpServer;
// m_log.InfoFormat("[CAPS] baked texture upload starting for {0}",newAssetID);
}
/// <summary>
/// Handle raw uploaded baked texture data.
/// </summary>
/// <param name="data"></param>
/// <param name="path"></param>
/// <param name="param"></param>
/// <returns></returns>
public string uploaderCaps(byte[] data, string path, string param)
{
Action<UUID, byte[]> handlerUpLoad = OnUpLoad;
// Don't do this asynchronously, otherwise it's possible for the client to send set appearance information
// on another thread which might send out avatar updates before the asset has been put into the asset
// service.
if (handlerUpLoad != null)
handlerUpLoad(newAssetID, data);
string res = String.Empty;
LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete();
uploadComplete.new_asset = newAssetID.ToString();
uploadComplete.new_inventory_item = UUID.Zero;
uploadComplete.state = "complete";
res = LLSDHelpers.SerialiseLLSDReply(uploadComplete);
httpListener.RemoveStreamHandler("POST", uploaderPath);
// m_log.DebugFormat("[BAKED TEXTURE UPLOADER]: baked texture upload completed for {0}", newAssetID);
return res;
}
}
}

View File

@ -0,0 +1,438 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Framework.Capabilities;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Services.Interfaces;
using Caps = OpenSim.Framework.Capabilities.Caps;
namespace OpenSim.Capabilities.Handlers
{
public class WebFetchInvDescHandler
{
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IInventoryService m_InventoryService;
private ILibraryService m_LibraryService;
// private object m_fetchLock = new Object();
public WebFetchInvDescHandler(IInventoryService invService, ILibraryService libService)
{
m_InventoryService = invService;
m_LibraryService = libService;
}
public string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
// lock (m_fetchLock)
// {
// m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Received request {0}", request);
// nasty temporary hack here, the linden client falsely
// identifies the uuid 00000000-0000-0000-0000-000000000000
// as a string which breaks us
//
// correctly mark it as a uuid
//
request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
// another hack <integer>1</integer> results in a
// System.ArgumentException: Object type System.Int32 cannot
// be converted to target type: System.Boolean
//
request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
Hashtable hash = new Hashtable();
try
{
hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
}
catch (LLSD.LLSDParseException e)
{
m_log.ErrorFormat("[WEB FETCH INV DESC HANDLER]: Fetch error: {0}{1}" + e.Message, e.StackTrace);
m_log.Error("Request: " + request);
}
ArrayList foldersrequested = (ArrayList)hash["folders"];
string response = "";
for (int i = 0; i < foldersrequested.Count; i++)
{
string inventoryitemstr = "";
Hashtable inventoryhash = (Hashtable)foldersrequested[i];
LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents();
try
{
LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest);
}
catch (Exception e)
{
m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e);
}
LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest);
inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
response += inventoryitemstr;
}
if (response.Length == 0)
{
// Ter-guess: If requests fail a lot, the client seems to stop requesting descendants.
// Therefore, I'm concluding that the client only has so many threads available to do requests
// and when a thread stalls.. is stays stalled.
// Therefore we need to return something valid
response = "<llsd><map><key>folders</key><array /></map></llsd>";
}
else
{
response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
}
// m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request");
//m_log.Debug("[WEB FETCH INV DESC HANDLER] "+response);
return response;
// }
}
/// <summary>
/// Construct an LLSD reply packet to a CAPS inventory request
/// </summary>
/// <param name="invFetch"></param>
/// <returns></returns>
private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch)
{
LLSDInventoryDescendents reply = new LLSDInventoryDescendents();
LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents();
contents.agent_id = invFetch.owner_id;
contents.owner_id = invFetch.owner_id;
contents.folder_id = invFetch.folder_id;
reply.folders.Array.Add(contents);
InventoryCollection inv = new InventoryCollection();
inv.Folders = new List<InventoryFolderBase>();
inv.Items = new List<InventoryItemBase>();
int version = 0;
int descendents = 0;
inv
= Fetch(
invFetch.owner_id, invFetch.folder_id, invFetch.owner_id,
invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version, out descendents);
if (inv.Folders != null)
{
foreach (InventoryFolderBase invFolder in inv.Folders)
{
contents.categories.Array.Add(ConvertInventoryFolder(invFolder));
}
descendents += inv.Folders.Count;
}
if (inv.Items != null)
{
foreach (InventoryItemBase invItem in inv.Items)
{
contents.items.Array.Add(ConvertInventoryItem(invItem));
}
}
contents.descendents = descendents;
contents.version = version;
// m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Replying to request for folder {0} (fetch items {1}, fetch folders {2}) with {3} items and {4} folders for agent {5}",
// invFetch.folder_id,
// invFetch.fetch_items,
// invFetch.fetch_folders,
// contents.items.Array.Count,
// contents.categories.Array.Count,
// invFetch.owner_id);
return reply;
}
/// <summary>
/// Handle the caps inventory descendents fetch.
/// </summary>
/// <param name="agentID"></param>
/// <param name="folderID"></param>
/// <param name="ownerID"></param>
/// <param name="fetchFolders"></param>
/// <param name="fetchItems"></param>
/// <param name="sortOrder"></param>
/// <param name="version"></param>
/// <returns>An empty InventoryCollection if the inventory look up failed</returns>
private InventoryCollection Fetch(
UUID agentID, UUID folderID, UUID ownerID,
bool fetchFolders, bool fetchItems, int sortOrder, out int version, out int descendents)
{
// m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Fetching folders ({0}), items ({1}) from {2} for agent {3}",
// fetchFolders, fetchItems, folderID, agentID);
// FIXME MAYBE: We're not handling sortOrder!
version = 0;
descendents = 0;
InventoryFolderImpl fold;
if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null && agentID == m_LibraryService.LibraryRootFolder.Owner)
{
if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(folderID)) != null)
{
InventoryCollection ret = new InventoryCollection();
ret.Folders = new List<InventoryFolderBase>();
ret.Items = fold.RequestListOfItems();
descendents = ret.Folders.Count + ret.Items.Count;
return ret;
}
}
InventoryCollection contents = new InventoryCollection();
if (folderID != UUID.Zero)
{
contents = m_InventoryService.GetFolderContent(agentID, folderID);
InventoryFolderBase containingFolder = new InventoryFolderBase();
containingFolder.ID = folderID;
containingFolder.Owner = agentID;
containingFolder = m_InventoryService.GetFolder(containingFolder);
if (containingFolder != null)
{
// m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Retrieved folder {0} {1} for agent id {2}",
// containingFolder.Name, containingFolder.ID, agentID);
version = containingFolder.Version;
if (fetchItems)
{
List<InventoryItemBase> itemsToReturn = contents.Items;
List<InventoryItemBase> originalItems = new List<InventoryItemBase>(itemsToReturn);
// descendents must only include the links, not the linked items we add
descendents = originalItems.Count;
// Add target items for links in this folder before the links themselves.
foreach (InventoryItemBase item in originalItems)
{
if (item.AssetType == (int)AssetType.Link)
{
InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID));
// Take care of genuinely broken links where the target doesn't exist
// HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
// but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
// rather than having to keep track of every folder requested in the recursion.
if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
itemsToReturn.Insert(0, linkedItem);
}
}
// Now scan for folder links and insert the items they target and those links at the head of the return data
foreach (InventoryItemBase item in originalItems)
{
if (item.AssetType == (int)AssetType.LinkFolder)
{
InventoryCollection linkedFolderContents = m_InventoryService.GetFolderContent(ownerID, item.AssetID);
List<InventoryItemBase> links = linkedFolderContents.Items;
itemsToReturn.InsertRange(0, links);
foreach (InventoryItemBase link in linkedFolderContents.Items)
{
// Take care of genuinely broken links where the target doesn't exist
// HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
// but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
// rather than having to keep track of every folder requested in the recursion.
if (link != null)
{
// m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Adding item {0} {1} from folder {2} linked from {3}",
// link.Name, (AssetType)link.AssetType, item.AssetID, containingFolder.Name);
InventoryItemBase linkedItem
= m_InventoryService.GetItem(new InventoryItemBase(link.AssetID));
if (linkedItem != null)
itemsToReturn.Insert(0, linkedItem);
}
}
}
}
}
// foreach (InventoryItemBase item in contents.Items)
// {
// m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Returning item {0}, type {1}, parent {2} in {3} {4}",
// item.Name, (AssetType)item.AssetType, item.Folder, containingFolder.Name, containingFolder.ID);
// }
// =====
//
// foreach (InventoryItemBase linkedItem in linkedItemsToAdd)
// {
// m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Inserted linked item {0} for link in folder {1} for agent {2}",
// linkedItem.Name, folderID, agentID);
//
// contents.Items.Add(linkedItem);
// }
//
// // If the folder requested contains links, then we need to send those folders first, otherwise the links
// // will be broken in the viewer.
// HashSet<UUID> linkedItemFolderIdsToSend = new HashSet<UUID>();
// foreach (InventoryItemBase item in contents.Items)
// {
// if (item.AssetType == (int)AssetType.Link)
// {
// InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID));
//
// // Take care of genuinely broken links where the target doesn't exist
// // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
// // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
// // rather than having to keep track of every folder requested in the recursion.
// if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
// {
// // We don't need to send the folder if source and destination of the link are in the same
// // folder.
// if (linkedItem.Folder != containingFolder.ID)
// linkedItemFolderIdsToSend.Add(linkedItem.Folder);
// }
// }
// }
//
// foreach (UUID linkedItemFolderId in linkedItemFolderIdsToSend)
// {
// m_log.DebugFormat(
// "[WEB FETCH INV DESC HANDLER]: Recursively fetching folder {0} linked by item in folder {1} for agent {2}",
// linkedItemFolderId, folderID, agentID);
//
// int dummyVersion;
// InventoryCollection linkedCollection
// = Fetch(
// agentID, linkedItemFolderId, ownerID, fetchFolders, fetchItems, sortOrder, out dummyVersion);
//
// InventoryFolderBase linkedFolder = new InventoryFolderBase(linkedItemFolderId);
// linkedFolder.Owner = agentID;
// linkedFolder = m_InventoryService.GetFolder(linkedFolder);
//
//// contents.Folders.AddRange(linkedCollection.Folders);
//
// contents.Folders.Add(linkedFolder);
// contents.Items.AddRange(linkedCollection.Items);
// }
// }
}
}
else
{
// Lost items don't really need a version
version = 1;
}
return contents;
}
/// <summary>
/// Convert an internal inventory folder object into an LLSD object.
/// </summary>
/// <param name="invFolder"></param>
/// <returns></returns>
private LLSDInventoryFolder ConvertInventoryFolder(InventoryFolderBase invFolder)
{
LLSDInventoryFolder llsdFolder = new LLSDInventoryFolder();
llsdFolder.folder_id = invFolder.ID;
llsdFolder.parent_id = invFolder.ParentID;
llsdFolder.name = invFolder.Name;
llsdFolder.type = invFolder.Type;
llsdFolder.preferred_type = -1;
return llsdFolder;
}
/// <summary>
/// Convert an internal inventory item object into an LLSD object.
/// </summary>
/// <param name="invItem"></param>
/// <returns></returns>
private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem)
{
LLSDInventoryItem llsdItem = new LLSDInventoryItem();
llsdItem.asset_id = invItem.AssetID;
llsdItem.created_at = invItem.CreationDate;
llsdItem.desc = invItem.Description;
llsdItem.flags = (int)invItem.Flags;
llsdItem.item_id = invItem.ID;
llsdItem.name = invItem.Name;
llsdItem.parent_id = invItem.Folder;
llsdItem.type = invItem.AssetType;
llsdItem.inv_type = invItem.InvType;
llsdItem.permissions = new LLSDPermissions();
llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid;
llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions;
llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions;
llsdItem.permissions.group_id = invItem.GroupID;
llsdItem.permissions.group_mask = (int)invItem.GroupPermissions;
llsdItem.permissions.is_owner_group = invItem.GroupOwned;
llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions;
llsdItem.permissions.owner_id = invItem.Owner;
llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions;
llsdItem.sale_info = new LLSDSaleInfo();
llsdItem.sale_info.sale_price = invItem.SalePrice;
llsdItem.sale_info.sale_type = invItem.SaleType;
return llsdItem;
}
}
}

View File

@ -29,22 +29,19 @@ using System;
using Nini.Config;
using OpenSim.Server.Base;
using OpenSim.Services.Interfaces;
using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Server.Handlers.Base;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace OpenSim.Capabilities.Handlers
{
public class FetchInvDescServerConnector : ServiceConnector
public class WebFetchInvDescServerConnector : ServiceConnector
{
private IInventoryService m_InventoryService;
private ILibraryService m_LibraryService;
private string m_ConfigName = "CapsService";
public FetchInvDescServerConnector(IConfigSource config, IHttpServer server, string configName) :
public WebFetchInvDescServerConnector(IConfigSource config, IHttpServer server, string configName) :
base(config, server, configName)
{
if (configName != String.Empty)
@ -70,16 +67,16 @@ namespace OpenSim.Capabilities.Handlers
m_LibraryService =
ServerUtils.LoadPlugin<ILibraryService>(libService, args);
ExpiringKey<UUID> m_badRequests = new ExpiringKey<UUID>(30000);
FetchInvDescHandler webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, null);
ISimpleStreamHandler reqHandler
= new SimpleStreamHandler("/CAPS/WebFetchInvDesc/", delegate(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
webFetchHandler.FetchInventoryDescendentsRequest(httpRequest, httpResponse, m_badRequests);
});
server.AddSimpleStreamHandler(reqHandler);
WebFetchInvDescHandler webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
IRequestHandler reqHandler
= new RestStreamHandler(
"POST",
"/CAPS/WebFetchInvDesc/" /*+ UUID.Random()*/,
webFetchHandler.FetchInventoryDescendentsRequest,
"WebFetchInvDesc",
null);
server.AddStreamHandler(reqHandler);
}
}
}

View File

@ -68,10 +68,7 @@ namespace OpenSim.Framework.Capabilities
/// <returns></returns>
public static object LLSDDeserialize(byte[] b)
{
using (MemoryStream ms = new MemoryStream(b, false))
{
return LLSDDeserialize(ms);
}
return LLSDDeserialize(new MemoryStream(b, false));
}
/// <summary>
@ -81,23 +78,21 @@ namespace OpenSim.Framework.Capabilities
/// <returns></returns>
public static object LLSDDeserialize(Stream st)
{
using (XmlTextReader reader = new XmlTextReader(st))
{
reader.Read();
SkipWS(reader);
XmlTextReader reader = new XmlTextReader(st);
reader.Read();
SkipWS(reader);
if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "llsd")
throw new LLSDParseException("Expected <llsd>");
if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "llsd")
throw new LLSDParseException("Expected <llsd>");
reader.Read();
object ret = LLSDParseOne(reader);
SkipWS(reader);
reader.Read();
object ret = LLSDParseOne(reader);
SkipWS(reader);
if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "llsd")
throw new LLSDParseException("Expected </llsd>");
if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "llsd")
throw new LLSDParseException("Expected </llsd>");
return ret;
}
return ret;
}
/// <summary>
@ -107,17 +102,17 @@ namespace OpenSim.Framework.Capabilities
/// <returns></returns>
public static byte[] LLSDSerialize(object obj)
{
using(StringWriter sw = new StringWriter())
using(XmlTextWriter writer = new XmlTextWriter(sw))
{
writer.Formatting = Formatting.None;
StringWriter sw = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(sw);
writer.Formatting = Formatting.None;
writer.WriteStartElement(String.Empty, "llsd", String.Empty);
LLSDWriteOne(writer, obj);
writer.WriteEndElement();
writer.Flush();
return Util.UTF8.GetBytes(sw.ToString());
}
writer.WriteStartElement(String.Empty, "llsd", String.Empty);
LLSDWriteOne(writer, obj);
writer.WriteEndElement();
writer.Close();
return Util.UTF8.GetBytes(sw.ToString());
}
/// <summary>
@ -566,7 +561,7 @@ namespace OpenSim.Framework.Capabilities
endPos = FindEnd(llsd, 1);
if (Double.TryParse(llsd.Substring(1, endPos - 1), NumberStyles.Float,
Culture.NumberFormatInfo, out value))
Utils.EnUsCulture.NumberFormat, out value))
return value;
else
throw new LLSDParseException("Failed to parse double value type");

View File

@ -30,20 +30,13 @@ using OpenMetaverse;
namespace OpenSim.Framework.Capabilities
{
[LLSDType("MAP")]
public class LLSDAssetUploadComplete
{
public string new_asset = String.Empty;
public UUID new_inventory_item = UUID.Zero;
// public UUID new_texture_folder_id = UUID.Zero;
public string state = String.Empty;
public LLSDAssetUploadError error = null;
//public bool success = false;
public int new_next_owner_mask = 0;
public int new_group_mask = 0;
public int new_everyone_mask = 0;
public int inventory_item_flags = 0;
public LLSDAssetUploadComplete()
{

View File

@ -30,28 +30,15 @@ using OpenMetaverse;
namespace OpenSim.Framework.Capabilities
{
[OSDMap]
public class LLSDAssetResource
{
public OSDArray instance_list = new OSDArray();
public OSDArray texture_list = new OSDArray();
public OSDArray mesh_list = new OSDArray();
public string metric = String.Empty;
}
[OSDMap]
public class LLSDAssetUploadRequest
{
public string asset_type = String.Empty;
public string description = String.Empty;
public UUID folder_id = UUID.Zero;
public UUID texture_folder_id = UUID.Zero;
public int next_owner_mask = 0;
public int group_mask = 0;
public int everyone_mask = 0;
public string inventory_type = String.Empty;
public string name = String.Empty;
public LLSDAssetResource asset_resources = new LLSDAssetResource();
public LLSDAssetUploadRequest()
{
}

View File

@ -26,51 +26,20 @@
*/
using System;
using OpenMetaverse;
namespace OpenSim.Framework.Capabilities
{
[OSDMap]
public class LLSDAssetUploadError
{
public string message = String.Empty;
public UUID identifier = UUID.Zero;
}
[OSDMap]
public class LLSDAssetUploadResponsePricebrkDown
{
public int mesh_streaming;
public int mesh_physics;
public int mesh_instance;
public int texture;
public int model;
}
[OSDMap]
public class LLSDAssetUploadResponseData
{
public double resource_cost;
public double model_streaming_cost;
public double simulation_cost;
public double physics_cost;
public LLSDAssetUploadResponsePricebrkDown upload_price_breakdown = new LLSDAssetUploadResponsePricebrkDown();
}
[OSDMap]
public class LLSDAssetUploadResponse
{
public string uploader = String.Empty;
public string state = String.Empty;
public int upload_price = 0;
public LLSDAssetUploadResponseData data = null;
public LLSDAssetUploadError error = null;
public LLSDAssetUploadResponse()
{
}
}
[OSDMap]
public class LLSDNewFileAngentInventoryVariablePriceReplyResponse
{
@ -78,7 +47,7 @@ namespace OpenSim.Framework.Capabilities
public string state;
public int upload_price;
public string rsvp;
public LLSDNewFileAngentInventoryVariablePriceReplyResponse()
{
state = "confirm_upload";

View File

@ -30,7 +30,6 @@ using System.Collections;
using System.IO;
using System.Reflection;
using System.Xml;
using OpenMetaverse;
namespace OpenSim.Framework.Capabilities
{
@ -41,32 +40,17 @@ namespace OpenSim.Framework.Capabilities
public static string SerialiseLLSDReply(object obj)
{
using(StringWriter sw = new StringWriter())
using(XmlTextWriter writer = new XmlTextWriter(sw))
{
writer.Formatting = Formatting.None;
writer.WriteStartElement(String.Empty, "llsd", String.Empty);
SerializeOSDType(writer, obj);
writer.WriteEndElement();
writer.Flush();
StringWriter sw = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(sw);
writer.Formatting = Formatting.None;
writer.WriteStartElement(String.Empty, "llsd", String.Empty);
SerializeOSDType(writer, obj);
writer.WriteEndElement();
writer.Close();
//m_log.DebugFormat("[LLSD Helpers]: Generated serialized LLSD reply {0}", sw.ToString());
return sw.ToString();
}
}
public static string SerialiseLLSDReplyNoHeader(object obj)
{
using(StringWriter sw = new StringWriter())
using(XmlTextWriter writer = new XmlTextWriter(sw))
{
writer.Formatting = Formatting.None;
SerializeOSDType(writer, obj);
writer.Flush();
//m_log.DebugFormat("[LLSD Helpers]: Generated serialized LLSD reply {0}", sw.ToString());
return sw.ToString();
}
return sw.ToString();
}
private static void SerializeOSDType(XmlTextWriter writer, object obj)
@ -173,22 +157,6 @@ namespace OpenSim.Framework.Capabilities
// the LLSD map/array types in the array need to be deserialised
// but first we need to know the right class to deserialise them into.
}
else if(enumerator.Value is Boolean && field.FieldType == typeof(int) )
{
int i = (bool)enumerator.Value ? 1 : 0;
field.SetValue(obj, i);
}
else if(field.FieldType == typeof(bool) && enumerator.Value is int)
{
bool b = (int)enumerator.Value != 0;
field.SetValue(obj, b);
}
else if(field.FieldType == typeof(UUID) && enumerator.Value is string)
{
UUID u;
UUID.TryParse((string)enumerator.Value, out u);
field.SetValue(obj, u);
}
else
{
field.SetValue(obj, enumerator.Value);

View File

@ -37,6 +37,5 @@ namespace OpenSim.Framework.Capabilities
public string name;
public int type;
public int preferred_type;
public int version;
}
}

View File

@ -87,12 +87,12 @@ namespace OpenSim.Framework.Capabilities
[OSDMap]
public class LLSDInventoryFolderContents
{
public UUID agent_id;
public UUID agent_id;
public int descendents;
public UUID folder_id;
public UUID folder_id;
public OSDArray categories = new OSDArray();
public OSDArray items = new OSDArray();
public UUID owner_id;
public UUID owner_id;
public int version;
}

View File

@ -25,15 +25,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using OpenMetaverse;
namespace Robust32
namespace OpenSim.Framework.Capabilities
{
class Program
[OSDMap]
public class LLSDItemUpdate
{
static void Main(string[] args)
public UUID item_id;
public LLSDItemUpdate()
{
global::OpenSim.Server.OpenSimServer.Main(args);
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
using System.Collections;
namespace OpenSim.Framework.Capabilities
{
[OSDMap]
public class LLSDParcelVoiceInfoResponse
{
public int parcel_local_id;
public string region_name;
public Hashtable voice_credentials;
public LLSDParcelVoiceInfoResponse()
{
}
public LLSDParcelVoiceInfoResponse(string region, int localID, Hashtable creds)
{
region_name = region;
parcel_local_id = localID;
voice_credentials = creds;
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
using OpenMetaverse;
namespace OpenSim.Framework.Capabilities
{
[LLSDType("MAP")]
public class LLSDRemoteParcelResponse
{
public UUID parcel_id;
public LLSDRemoteParcelResponse()
{
}
}
}

View File

@ -48,7 +48,7 @@ namespace OpenSim.Framework.Capabilities
m_method = method;
}
protected override byte[] ProcessRequest(string path, Stream request,
public override byte[] Handle(string path, Stream request,
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{
//Encoding encoding = Util.UTF8;
@ -61,9 +61,6 @@ namespace OpenSim.Framework.Capabilities
// OpenMetaverse.StructuredData.LLSDParser.DeserializeXml(new XmlTextReader(request));
Hashtable hash = (Hashtable) LLSD.LLSDDeserialize(request);
if(hash == null)
return new byte[0];
TRequest llsdRequest = new TRequest();
LLSDHelpers.DeserialiseOSDMap(hash, llsdRequest);

View File

@ -30,22 +30,21 @@ using OpenMetaverse;
namespace OpenSim.Framework.Capabilities
{
[OSDMap]
public class LLSDAvatarPicker
public class LLSDTaskScriptUpdate
{
public string next_page_url;
// an array of LLSDPerson
public OSDArray agents = new OSDArray();
}
/// <summary>
/// The item containing the script to update
/// </summary>
public UUID item_id;
[OSDMap]
public class LLSDPerson
{
public string username;
public string display_name;
//'display_name_next_update':d"1970-01-01T00:00:00Z"
public string legacy_first_name;
public string legacy_last_name;
public UUID id;
public bool is_display_name_default;
/// <summary>
/// The task containing the script
/// </summary>
public UUID task_id;
/// <summary>
/// Signals whether the script is currently active
/// </summary>
public int is_script_running;
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OpenSim.Framework.Capabilities
{
[OSDMap]
public class LLSDVoiceAccountResponse
{
public string username;
public string password;
public string voice_sip_uri_hostname;
public string voice_account_server_name;
public LLSDVoiceAccountResponse()
{
}
public LLSDVoiceAccountResponse(string user, string pass)
{
username = user;
password = pass;
}
public LLSDVoiceAccountResponse(string user, string pass, string sipUriHost, string accountServer)
{
username = user;
password = pass;
voice_sip_uri_hostname = sipUriHost;
voice_account_server_name = accountServer;
}
}
}

View File

@ -1,33 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenSim.Capabilities")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("http://opensimulator.org")]
[assembly: AssemblyProduct("OpenSim")]
[assembly: AssemblyCopyright("OpenSimulator developers")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7d1a55b1-8fab-42ff-9c83-066a9cc34d76")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("0.7.6.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -80,7 +80,7 @@ namespace OpenSim.ConsoleClient
while (m_Server.Running)
{
System.Threading.Thread.Sleep(500);
MainConsole.Instance.Prompt();
// MainConsole.Instance.Prompt();
}
if (pidFile != String.Empty)
@ -178,7 +178,7 @@ namespace OpenSim.ConsoleClient
Requester.MakeRequest(requestUrl, requestData, ReadResponses);
return;
}
List<string> lines = new List<string>();
foreach (XmlNode part in rootNodeL[0].ChildNodes)
@ -202,7 +202,7 @@ namespace OpenSim.ConsoleClient
string[] parts = l.Split(new char[] {':'}, 3);
if (parts.Length != 3)
continue;
if (parts[2].StartsWith("+++") || parts[2].StartsWith("-++"))
prompt = parts[2];
else

View File

@ -1,33 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("OpenSim.ConsoleClient")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("http://opensimulator.org")]
[assembly: AssemblyProduct("OpenSim")]
[assembly: AssemblyCopyright("OpenSimulator developers")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8945df94-2e5e-475b-88fa-35a7cdde6fd7")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("0.7.6.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -44,6 +44,7 @@ namespace OpenSim.ConsoleClient
ReplyDelegate action)
{
WebRequest request = WebRequest.Create(requestUrl);
WebResponse response = null;
request.Method = "POST";
@ -63,18 +64,16 @@ namespace OpenSim.ConsoleClient
{
string reply = String.Empty;
using (WebResponse response = request.EndGetResponse(ar))
{
try
{
using (Stream s = response.GetResponseStream())
using (StreamReader r = new StreamReader(s))
reply = r.ReadToEnd();
response = request.EndGetResponse(ar);
}
catch (System.InvalidOperationException)
{
}
try
{
StreamReader r = new StreamReader(response.GetResponseStream());
reply = r.ReadToEnd();
}
catch (System.InvalidOperationException)
{
}
action(requestUrl, data, reply);

View File

@ -37,8 +37,9 @@ namespace OpenSim.Data
public abstract class AssetDataBase : IAssetDataPlugin
{
public abstract AssetBase GetAsset(UUID uuid);
public abstract bool StoreAsset(AssetBase asset);
public abstract bool[] AssetsExist(UUID[] uuids);
public abstract void StoreAsset(AssetBase asset);
public abstract bool ExistsAsset(UUID uuid);
public abstract List<AssetMetadata> FetchAssetMetadataSet(int start, int count);

View File

@ -38,7 +38,7 @@ namespace OpenSim.Data
/// <summary>This function converts a value returned from the database in one of the
/// supported formats into a UUID. This function is not actually DBMS-specific right
/// now
///
///
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@ -47,25 +47,24 @@ namespace OpenSim.Data
if ((id == null) || (id == DBNull.Value))
return UUID.Zero;
Type idtype = id.GetType();
if (idtype == typeof(Guid))
if (id.GetType() == typeof(Guid))
return new UUID((Guid)id);
if (id.GetType() == typeof(string))
if (id.GetType() == typeof(byte[]))
{
Guid gg;
if (Guid.TryParse((string)id, out gg))
return new UUID(gg);
return UUID.Zero;
if (((byte[])id).Length == 0)
return UUID.Zero;
else if (((byte[])id).Length == 16)
return new UUID((byte[])id, 0);
}
else if (id.GetType() == typeof(string))
{
if (((string)id).Length == 0)
return UUID.Zero;
else if (((string)id).Length == 36)
return new UUID((string)id);
}
if (idtype == typeof(byte[]))
{
if (((byte[])id).Length < 16)
return UUID.Zero;
return new UUID((byte[])id, 0);
}
throw new Exception("Failed to convert db value to UUID: " + id.ToString());
}
}

View File

@ -34,8 +34,8 @@ namespace OpenSim.Data
public interface IAssetDataPlugin : IPlugin
{
AssetBase GetAsset(UUID uuid);
bool StoreAsset(AssetBase asset);
bool[] AssetsExist(UUID[] uuids);
void StoreAsset(AssetBase asset);
bool ExistsAsset(UUID uuid);
List<AssetMetadata> FetchAssetMetadataSet(int start, int count);
void Initialise(string connect);
bool Delete(string id);

View File

@ -39,7 +39,7 @@ namespace OpenSim.Data
public Dictionary<string, string> Data;
}
public interface IAvatarData
public interface IAvatarData
{
AvatarBaseData[] Get(string field, string val);
bool Store(AvatarBaseData data);

Some files were not shown because too many files have changed in this diff Show More